Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1 - #93

Merged
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master
Aug 19, 2026
Merged

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1#93
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master

Conversation

@septicwolf818

Copy link
Copy Markdown
Contributor

Servers with SDK protocol >= 5 serialize a matrix map block for every zone, not just MATRIX zones. ZoneData.unpack only consumed it when zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE zones and producing garbage values (e.g. "256 is not a valid ZoneType") during device discovery.

Consume the matrix block whenever matrix_zone_size > 0, which is backward compatible since the size is 0 when no map exists. Also widen ZoneType to the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y, SEGMENTED) and add a missing fallback so future types don't crash.

septicwolf818and others added 2 commits August 11, 2026 22:57
Servers with SDK protocol >= 5 serialize a matrix map block for every
zone, not just MATRIX zones. ZoneData.unpack only consumed it when
zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE
zones and producing garbage values (e.g. "256 is not a valid ZoneType")
during device discovery.
Consume the matrix block whenever matrix_zone_size > 0, which is backward
compatible since the size is 0 when no map exists. Also widen ZoneType to
the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y,
SEGMENTED) and add a _missing_ fallback so future types don't crash.
Fix zone parsing desync on OpenRGB 1.0rc3+ servers
@CalcProgrammer1

Copy link
Copy Markdown

Is this only on pipeline? If so it's a bug and needs fixed OpenRGB side, the 5 and earlier protocols should not be changed. If backwards compatibility broke we need to fix it.

@CalcProgrammer1

Copy link
Copy Markdown

Looking at the older OpenRGB code, it actually sends matrix map only if a matrix map exists, not necessarily if type is matrix. It does so happen that 99 if not 100% of controllers that had a non-null matrix map pointer also had ZONE_TYPE_MATRIX, but the old implementation of the server didn't necessarily guarantee this relationship either so in this case I'd say the parser here is in the wrong.

For instance, in the 0.8 release's client side parser, you can see it checks the matrix map size to determine whether or not to parse a matrix map, not the type value:

https://gitlab.com/CalcProgrammer1/OpenRGB/-/blob/release_0.8/RGBController/RGBController.cpp?ref_type=tags#L742

On OpenRGB next (the code that was merged after 1.0rc3 and will soon be released as 1.0), matrix maps are no longer handled as pointers, so every zone "has" a matrix map. I could update the OpenRGB side to not send empty matrix maps (w 0, h 0, map size 0) which would match the old behavior better, save bytes on the network, and reduce breakage caused by this issue. I think I will do this, but this issue should still be fixed here as it was working by happenstance as is.

septicwolf818and others added 2 commits August 18, 2026 10:57
ZoneData.pack wrote the element count in the matrix size field instead of
the byte length the protocol defines (8 + w*h*4), and struct.pack used
native alignment which inserted 2 phantom bytes after the size field,
shifting height/width and desyncing any packed matrix zone. Also crashed
on None values (0xFFFFFFFF map holes converted by unpack) and on zones
with no map (mat_height/mat_width None), breaking save_profile(local=True).
Use a packed layout with the correct byte length, map None back to
0xFFFFFFFF, and skip empty maps like the server does.
Align matrix map serialization with the OpenRGB protocol
@septicwolf818

Copy link
Copy Markdown
ContributorAuthor

@CalcProgrammer1 Yes, it's only on next (the post-1.0rc3 pipeline). In release_0.8 and earlier, matrix_map is a pointer and the block is only written when it's non-NULL, regardless of zone type - so 5-and-earlier are untouched there. On next, matrix_map became a value member (matrix_map_type matrix_map, RGBControllerInterface.h:427), and GetMatrixMapDescriptionSize (RGBController.cpp:2792) isn't gated by protocol version, so a block is now sent for every zone at any negotiated protocol version (0-5 included). That does change the wire data for the older protocols when talking to a next server - so yes, it's a next-side backwards-compat break. Skipping empty maps (size 0 when h==0 && w==0) on next would restore the old behavior. I have aligned the python parser to key off the size field like the 0.8 client, so it works with both.

@CalcProgrammer1

Copy link
Copy Markdown

Pushed a fix on the OpenRGB side to not send empty maps, but this change still should be implemented on the Python side to correctly match OpenRGB's own parser.

@jath03

Copy link
Copy Markdown
Owner

Interesting. I wasn't aware that other zone types could have a matrix map

@jath03
jath03 merged commit f2eb04b into jath03:masterAug 19, 2026
4 checks passed
@CalcProgrammer1

Copy link
Copy Markdown

Interesting. I wasn't aware that other zone types could have a matrix map

In previous versions of OpenRGB the protocol did not exclude the possibility, but I don't know of any controllers that actually did it. On 1.0, I've expanded manually configurable zones (ARGB zones) such that the user has control over the Type field. They can set a matrix map and set the Type field as two independent user options. The matrix map is saved no matter what the type field is set to which allows toggling between matrix and non matrix layouts easily. That is the main place you will see this behavior in practice now.

@CalcProgrammer1

Copy link
Copy Markdown

It also now allows device controllers to build in their own matrix map but make it optional, a keyboard could have an underglow zone that could be switched between mapping into the keyboard matrix or working as an independent light ring.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@septicwolf818@CalcProgrammer1@jath03
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1 - #93

Merged
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master
Aug 19, 2026
Merged

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1#93
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master

Conversation

@septicwolf818

Copy link
Copy Markdown
Contributor

Servers with SDK protocol >= 5 serialize a matrix map block for every zone, not just MATRIX zones. ZoneData.unpack only consumed it when zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE zones and producing garbage values (e.g. "256 is not a valid ZoneType") during device discovery.

Consume the matrix block whenever matrix_zone_size > 0, which is backward compatible since the size is 0 when no map exists. Also widen ZoneType to the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y, SEGMENTED) and add a missing fallback so future types don't crash.

septicwolf818and others added 2 commits August 11, 2026 22:57
Servers with SDK protocol >= 5 serialize a matrix map block for every
zone, not just MATRIX zones. ZoneData.unpack only consumed it when
zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE
zones and producing garbage values (e.g. "256 is not a valid ZoneType")
during device discovery.
Consume the matrix block whenever matrix_zone_size > 0, which is backward
compatible since the size is 0 when no map exists. Also widen ZoneType to
the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y,
SEGMENTED) and add a _missing_ fallback so future types don't crash.
Fix zone parsing desync on OpenRGB 1.0rc3+ servers
@CalcProgrammer1

Copy link
Copy Markdown

Is this only on pipeline? If so it's a bug and needs fixed OpenRGB side, the 5 and earlier protocols should not be changed. If backwards compatibility broke we need to fix it.

@CalcProgrammer1

Copy link
Copy Markdown

Looking at the older OpenRGB code, it actually sends matrix map only if a matrix map exists, not necessarily if type is matrix. It does so happen that 99 if not 100% of controllers that had a non-null matrix map pointer also had ZONE_TYPE_MATRIX, but the old implementation of the server didn't necessarily guarantee this relationship either so in this case I'd say the parser here is in the wrong.

For instance, in the 0.8 release's client side parser, you can see it checks the matrix map size to determine whether or not to parse a matrix map, not the type value:

https://gitlab.com/CalcProgrammer1/OpenRGB/-/blob/release_0.8/RGBController/RGBController.cpp?ref_type=tags#L742

On OpenRGB next (the code that was merged after 1.0rc3 and will soon be released as 1.0), matrix maps are no longer handled as pointers, so every zone "has" a matrix map. I could update the OpenRGB side to not send empty matrix maps (w 0, h 0, map size 0) which would match the old behavior better, save bytes on the network, and reduce breakage caused by this issue. I think I will do this, but this issue should still be fixed here as it was working by happenstance as is.

septicwolf818and others added 2 commits August 18, 2026 10:57
ZoneData.pack wrote the element count in the matrix size field instead of
the byte length the protocol defines (8 + w*h*4), and struct.pack used
native alignment which inserted 2 phantom bytes after the size field,
shifting height/width and desyncing any packed matrix zone. Also crashed
on None values (0xFFFFFFFF map holes converted by unpack) and on zones
with no map (mat_height/mat_width None), breaking save_profile(local=True).
Use a packed layout with the correct byte length, map None back to
0xFFFFFFFF, and skip empty maps like the server does.
Align matrix map serialization with the OpenRGB protocol
@septicwolf818

Copy link
Copy Markdown
ContributorAuthor

@CalcProgrammer1 Yes, it's only on next (the post-1.0rc3 pipeline). In release_0.8 and earlier, matrix_map is a pointer and the block is only written when it's non-NULL, regardless of zone type - so 5-and-earlier are untouched there. On next, matrix_map became a value member (matrix_map_type matrix_map, RGBControllerInterface.h:427), and GetMatrixMapDescriptionSize (RGBController.cpp:2792) isn't gated by protocol version, so a block is now sent for every zone at any negotiated protocol version (0-5 included). That does change the wire data for the older protocols when talking to a next server - so yes, it's a next-side backwards-compat break. Skipping empty maps (size 0 when h==0 && w==0) on next would restore the old behavior. I have aligned the python parser to key off the size field like the 0.8 client, so it works with both.

@CalcProgrammer1

Copy link
Copy Markdown

Pushed a fix on the OpenRGB side to not send empty maps, but this change still should be implemented on the Python side to correctly match OpenRGB's own parser.

@jath03

Copy link
Copy Markdown
Owner

Interesting. I wasn't aware that other zone types could have a matrix map

@jath03
jath03 merged commit f2eb04b into jath03:masterAug 19, 2026
4 checks passed
@CalcProgrammer1

Copy link
Copy Markdown

Interesting. I wasn't aware that other zone types could have a matrix map

In previous versions of OpenRGB the protocol did not exclude the possibility, but I don't know of any controllers that actually did it. On 1.0, I've expanded manually configurable zones (ARGB zones) such that the user has control over the Type field. They can set a matrix map and set the Type field as two independent user options. The matrix map is saved no matter what the type field is set to which allows toggling between matrix and non matrix layouts easily. That is the main place you will see this behavior in practice now.

@CalcProgrammer1

Copy link
Copy Markdown

It also now allows device controllers to build in their own matrix map but make it optional, a keyboard could have an underglow zone that could be switched between mapping into the keyboard matrix or working as an independent light ring.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@septicwolf818@CalcProgrammer1@jath03
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1 - #93

Merged
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master
Aug 19, 2026
Merged

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1#93
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master

Conversation

@septicwolf818

Copy link
Copy Markdown
Contributor

Servers with SDK protocol >= 5 serialize a matrix map block for every zone, not just MATRIX zones. ZoneData.unpack only consumed it when zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE zones and producing garbage values (e.g. "256 is not a valid ZoneType") during device discovery.

Consume the matrix block whenever matrix_zone_size > 0, which is backward compatible since the size is 0 when no map exists. Also widen ZoneType to the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y, SEGMENTED) and add a missing fallback so future types don't crash.

septicwolf818and others added 2 commits August 11, 2026 22:57
Servers with SDK protocol >= 5 serialize a matrix map block for every
zone, not just MATRIX zones. ZoneData.unpack only consumed it when
zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE
zones and producing garbage values (e.g. "256 is not a valid ZoneType")
during device discovery.
Consume the matrix block whenever matrix_zone_size > 0, which is backward
compatible since the size is 0 when no map exists. Also widen ZoneType to
the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y,
SEGMENTED) and add a _missing_ fallback so future types don't crash.
Fix zone parsing desync on OpenRGB 1.0rc3+ servers
@CalcProgrammer1

Copy link
Copy Markdown

Is this only on pipeline? If so it's a bug and needs fixed OpenRGB side, the 5 and earlier protocols should not be changed. If backwards compatibility broke we need to fix it.

@CalcProgrammer1

Copy link
Copy Markdown

Looking at the older OpenRGB code, it actually sends matrix map only if a matrix map exists, not necessarily if type is matrix. It does so happen that 99 if not 100% of controllers that had a non-null matrix map pointer also had ZONE_TYPE_MATRIX, but the old implementation of the server didn't necessarily guarantee this relationship either so in this case I'd say the parser here is in the wrong.

For instance, in the 0.8 release's client side parser, you can see it checks the matrix map size to determine whether or not to parse a matrix map, not the type value:

https://gitlab.com/CalcProgrammer1/OpenRGB/-/blob/release_0.8/RGBController/RGBController.cpp?ref_type=tags#L742

On OpenRGB next (the code that was merged after 1.0rc3 and will soon be released as 1.0), matrix maps are no longer handled as pointers, so every zone "has" a matrix map. I could update the OpenRGB side to not send empty matrix maps (w 0, h 0, map size 0) which would match the old behavior better, save bytes on the network, and reduce breakage caused by this issue. I think I will do this, but this issue should still be fixed here as it was working by happenstance as is.

septicwolf818and others added 2 commits August 18, 2026 10:57
ZoneData.pack wrote the element count in the matrix size field instead of
the byte length the protocol defines (8 + w*h*4), and struct.pack used
native alignment which inserted 2 phantom bytes after the size field,
shifting height/width and desyncing any packed matrix zone. Also crashed
on None values (0xFFFFFFFF map holes converted by unpack) and on zones
with no map (mat_height/mat_width None), breaking save_profile(local=True).
Use a packed layout with the correct byte length, map None back to
0xFFFFFFFF, and skip empty maps like the server does.
Align matrix map serialization with the OpenRGB protocol
@septicwolf818

Copy link
Copy Markdown
ContributorAuthor

@CalcProgrammer1 Yes, it's only on next (the post-1.0rc3 pipeline). In release_0.8 and earlier, matrix_map is a pointer and the block is only written when it's non-NULL, regardless of zone type - so 5-and-earlier are untouched there. On next, matrix_map became a value member (matrix_map_type matrix_map, RGBControllerInterface.h:427), and GetMatrixMapDescriptionSize (RGBController.cpp:2792) isn't gated by protocol version, so a block is now sent for every zone at any negotiated protocol version (0-5 included). That does change the wire data for the older protocols when talking to a next server - so yes, it's a next-side backwards-compat break. Skipping empty maps (size 0 when h==0 && w==0) on next would restore the old behavior. I have aligned the python parser to key off the size field like the 0.8 client, so it works with both.

@CalcProgrammer1

Copy link
Copy Markdown

Pushed a fix on the OpenRGB side to not send empty maps, but this change still should be implemented on the Python side to correctly match OpenRGB's own parser.

@jath03

Copy link
Copy Markdown
Owner

Interesting. I wasn't aware that other zone types could have a matrix map

@jath03
jath03 merged commit f2eb04b into jath03:masterAug 19, 2026
4 checks passed
@CalcProgrammer1

Copy link
Copy Markdown

Interesting. I wasn't aware that other zone types could have a matrix map

In previous versions of OpenRGB the protocol did not exclude the possibility, but I don't know of any controllers that actually did it. On 1.0, I've expanded manually configurable zones (ARGB zones) such that the user has control over the Type field. They can set a matrix map and set the Type field as two independent user options. The matrix map is saved no matter what the type field is set to which allows toggling between matrix and non matrix layouts easily. That is the main place you will see this behavior in practice now.

@CalcProgrammer1

Copy link
Copy Markdown

It also now allows device controllers to build in their own matrix map but make it optional, a keyboard could have an underglow zone that could be switched between mapping into the keyboard matrix or working as an independent light ring.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@septicwolf818@CalcProgrammer1@jath03
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1 - #93

Merged
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master
Aug 19, 2026
Merged

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1#93
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master

Conversation

@septicwolf818

Copy link
Copy Markdown
Contributor

Servers with SDK protocol >= 5 serialize a matrix map block for every zone, not just MATRIX zones. ZoneData.unpack only consumed it when zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE zones and producing garbage values (e.g. "256 is not a valid ZoneType") during device discovery.

Consume the matrix block whenever matrix_zone_size > 0, which is backward compatible since the size is 0 when no map exists. Also widen ZoneType to the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y, SEGMENTED) and add a missing fallback so future types don't crash.

septicwolf818and others added 2 commits August 11, 2026 22:57
Servers with SDK protocol >= 5 serialize a matrix map block for every
zone, not just MATRIX zones. ZoneData.unpack only consumed it when
zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE
zones and producing garbage values (e.g. "256 is not a valid ZoneType")
during device discovery.
Consume the matrix block whenever matrix_zone_size > 0, which is backward
compatible since the size is 0 when no map exists. Also widen ZoneType to
the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y,
SEGMENTED) and add a _missing_ fallback so future types don't crash.
Fix zone parsing desync on OpenRGB 1.0rc3+ servers
@CalcProgrammer1

Copy link
Copy Markdown

Is this only on pipeline? If so it's a bug and needs fixed OpenRGB side, the 5 and earlier protocols should not be changed. If backwards compatibility broke we need to fix it.

@CalcProgrammer1

Copy link
Copy Markdown

Looking at the older OpenRGB code, it actually sends matrix map only if a matrix map exists, not necessarily if type is matrix. It does so happen that 99 if not 100% of controllers that had a non-null matrix map pointer also had ZONE_TYPE_MATRIX, but the old implementation of the server didn't necessarily guarantee this relationship either so in this case I'd say the parser here is in the wrong.

For instance, in the 0.8 release's client side parser, you can see it checks the matrix map size to determine whether or not to parse a matrix map, not the type value:

https://gitlab.com/CalcProgrammer1/OpenRGB/-/blob/release_0.8/RGBController/RGBController.cpp?ref_type=tags#L742

On OpenRGB next (the code that was merged after 1.0rc3 and will soon be released as 1.0), matrix maps are no longer handled as pointers, so every zone "has" a matrix map. I could update the OpenRGB side to not send empty matrix maps (w 0, h 0, map size 0) which would match the old behavior better, save bytes on the network, and reduce breakage caused by this issue. I think I will do this, but this issue should still be fixed here as it was working by happenstance as is.

septicwolf818and others added 2 commits August 18, 2026 10:57
ZoneData.pack wrote the element count in the matrix size field instead of
the byte length the protocol defines (8 + w*h*4), and struct.pack used
native alignment which inserted 2 phantom bytes after the size field,
shifting height/width and desyncing any packed matrix zone. Also crashed
on None values (0xFFFFFFFF map holes converted by unpack) and on zones
with no map (mat_height/mat_width None), breaking save_profile(local=True).
Use a packed layout with the correct byte length, map None back to
0xFFFFFFFF, and skip empty maps like the server does.
Align matrix map serialization with the OpenRGB protocol
@septicwolf818

Copy link
Copy Markdown
ContributorAuthor

@CalcProgrammer1 Yes, it's only on next (the post-1.0rc3 pipeline). In release_0.8 and earlier, matrix_map is a pointer and the block is only written when it's non-NULL, regardless of zone type - so 5-and-earlier are untouched there. On next, matrix_map became a value member (matrix_map_type matrix_map, RGBControllerInterface.h:427), and GetMatrixMapDescriptionSize (RGBController.cpp:2792) isn't gated by protocol version, so a block is now sent for every zone at any negotiated protocol version (0-5 included). That does change the wire data for the older protocols when talking to a next server - so yes, it's a next-side backwards-compat break. Skipping empty maps (size 0 when h==0 && w==0) on next would restore the old behavior. I have aligned the python parser to key off the size field like the 0.8 client, so it works with both.

@CalcProgrammer1

Copy link
Copy Markdown

Pushed a fix on the OpenRGB side to not send empty maps, but this change still should be implemented on the Python side to correctly match OpenRGB's own parser.

@jath03

Copy link
Copy Markdown
Owner

Interesting. I wasn't aware that other zone types could have a matrix map

@jath03
jath03 merged commit f2eb04b into jath03:masterAug 19, 2026
4 checks passed
@CalcProgrammer1

Copy link
Copy Markdown

Interesting. I wasn't aware that other zone types could have a matrix map

In previous versions of OpenRGB the protocol did not exclude the possibility, but I don't know of any controllers that actually did it. On 1.0, I've expanded manually configurable zones (ARGB zones) such that the user has control over the Type field. They can set a matrix map and set the Type field as two independent user options. The matrix map is saved no matter what the type field is set to which allows toggling between matrix and non matrix layouts easily. That is the main place you will see this behavior in practice now.

@CalcProgrammer1

Copy link
Copy Markdown

It also now allows device controllers to build in their own matrix map but make it optional, a keyboard could have an underglow zone that could be switched between mapping into the keyboard matrix or working as an independent light ring.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@septicwolf818@CalcProgrammer1@jath03
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1 - #93

Merged
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master
Aug 19, 2026
Merged

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1#93
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master

Conversation

@septicwolf818

Copy link
Copy Markdown
Contributor

Servers with SDK protocol >= 5 serialize a matrix map block for every zone, not just MATRIX zones. ZoneData.unpack only consumed it when zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE zones and producing garbage values (e.g. "256 is not a valid ZoneType") during device discovery.

Consume the matrix block whenever matrix_zone_size > 0, which is backward compatible since the size is 0 when no map exists. Also widen ZoneType to the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y, SEGMENTED) and add a missing fallback so future types don't crash.

septicwolf818and others added 2 commits August 11, 2026 22:57
Servers with SDK protocol >= 5 serialize a matrix map block for every
zone, not just MATRIX zones. ZoneData.unpack only consumed it when
zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE
zones and producing garbage values (e.g. "256 is not a valid ZoneType")
during device discovery.
Consume the matrix block whenever matrix_zone_size > 0, which is backward
compatible since the size is 0 when no map exists. Also widen ZoneType to
the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y,
SEGMENTED) and add a _missing_ fallback so future types don't crash.
Fix zone parsing desync on OpenRGB 1.0rc3+ servers
@CalcProgrammer1

Copy link
Copy Markdown

Is this only on pipeline? If so it's a bug and needs fixed OpenRGB side, the 5 and earlier protocols should not be changed. If backwards compatibility broke we need to fix it.

@CalcProgrammer1

Copy link
Copy Markdown

Looking at the older OpenRGB code, it actually sends matrix map only if a matrix map exists, not necessarily if type is matrix. It does so happen that 99 if not 100% of controllers that had a non-null matrix map pointer also had ZONE_TYPE_MATRIX, but the old implementation of the server didn't necessarily guarantee this relationship either so in this case I'd say the parser here is in the wrong.

For instance, in the 0.8 release's client side parser, you can see it checks the matrix map size to determine whether or not to parse a matrix map, not the type value:

https://gitlab.com/CalcProgrammer1/OpenRGB/-/blob/release_0.8/RGBController/RGBController.cpp?ref_type=tags#L742

On OpenRGB next (the code that was merged after 1.0rc3 and will soon be released as 1.0), matrix maps are no longer handled as pointers, so every zone "has" a matrix map. I could update the OpenRGB side to not send empty matrix maps (w 0, h 0, map size 0) which would match the old behavior better, save bytes on the network, and reduce breakage caused by this issue. I think I will do this, but this issue should still be fixed here as it was working by happenstance as is.

septicwolf818and others added 2 commits August 18, 2026 10:57
ZoneData.pack wrote the element count in the matrix size field instead of
the byte length the protocol defines (8 + w*h*4), and struct.pack used
native alignment which inserted 2 phantom bytes after the size field,
shifting height/width and desyncing any packed matrix zone. Also crashed
on None values (0xFFFFFFFF map holes converted by unpack) and on zones
with no map (mat_height/mat_width None), breaking save_profile(local=True).
Use a packed layout with the correct byte length, map None back to
0xFFFFFFFF, and skip empty maps like the server does.
Align matrix map serialization with the OpenRGB protocol
@septicwolf818

Copy link
Copy Markdown
ContributorAuthor

@CalcProgrammer1 Yes, it's only on next (the post-1.0rc3 pipeline). In release_0.8 and earlier, matrix_map is a pointer and the block is only written when it's non-NULL, regardless of zone type - so 5-and-earlier are untouched there. On next, matrix_map became a value member (matrix_map_type matrix_map, RGBControllerInterface.h:427), and GetMatrixMapDescriptionSize (RGBController.cpp:2792) isn't gated by protocol version, so a block is now sent for every zone at any negotiated protocol version (0-5 included). That does change the wire data for the older protocols when talking to a next server - so yes, it's a next-side backwards-compat break. Skipping empty maps (size 0 when h==0 && w==0) on next would restore the old behavior. I have aligned the python parser to key off the size field like the 0.8 client, so it works with both.

@CalcProgrammer1

Copy link
Copy Markdown

Pushed a fix on the OpenRGB side to not send empty maps, but this change still should be implemented on the Python side to correctly match OpenRGB's own parser.

@jath03

Copy link
Copy Markdown
Owner

Interesting. I wasn't aware that other zone types could have a matrix map

@jath03
jath03 merged commit f2eb04b into jath03:masterAug 19, 2026
4 checks passed
@CalcProgrammer1

Copy link
Copy Markdown

Interesting. I wasn't aware that other zone types could have a matrix map

In previous versions of OpenRGB the protocol did not exclude the possibility, but I don't know of any controllers that actually did it. On 1.0, I've expanded manually configurable zones (ARGB zones) such that the user has control over the Type field. They can set a matrix map and set the Type field as two independent user options. The matrix map is saved no matter what the type field is set to which allows toggling between matrix and non matrix layouts easily. That is the main place you will see this behavior in practice now.

@CalcProgrammer1

Copy link
Copy Markdown

It also now allows device controllers to build in their own matrix map but make it optional, a keyboard could have an underglow zone that could be switched between mapping into the keyboard matrix or working as an independent light ring.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@septicwolf818@CalcProgrammer1@jath03
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1 - #93

Merged
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master
Aug 19, 2026
Merged

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1#93
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master

Conversation

@septicwolf818

Copy link
Copy Markdown
Contributor

Servers with SDK protocol >= 5 serialize a matrix map block for every zone, not just MATRIX zones. ZoneData.unpack only consumed it when zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE zones and producing garbage values (e.g. "256 is not a valid ZoneType") during device discovery.

Consume the matrix block whenever matrix_zone_size > 0, which is backward compatible since the size is 0 when no map exists. Also widen ZoneType to the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y, SEGMENTED) and add a missing fallback so future types don't crash.

septicwolf818and others added 2 commits August 11, 2026 22:57
Servers with SDK protocol >= 5 serialize a matrix map block for every
zone, not just MATRIX zones. ZoneData.unpack only consumed it when
zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE
zones and producing garbage values (e.g. "256 is not a valid ZoneType")
during device discovery.
Consume the matrix block whenever matrix_zone_size > 0, which is backward
compatible since the size is 0 when no map exists. Also widen ZoneType to
the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y,
SEGMENTED) and add a _missing_ fallback so future types don't crash.
Fix zone parsing desync on OpenRGB 1.0rc3+ servers
@CalcProgrammer1

Copy link
Copy Markdown

Is this only on pipeline? If so it's a bug and needs fixed OpenRGB side, the 5 and earlier protocols should not be changed. If backwards compatibility broke we need to fix it.

@CalcProgrammer1

Copy link
Copy Markdown

Looking at the older OpenRGB code, it actually sends matrix map only if a matrix map exists, not necessarily if type is matrix. It does so happen that 99 if not 100% of controllers that had a non-null matrix map pointer also had ZONE_TYPE_MATRIX, but the old implementation of the server didn't necessarily guarantee this relationship either so in this case I'd say the parser here is in the wrong.

For instance, in the 0.8 release's client side parser, you can see it checks the matrix map size to determine whether or not to parse a matrix map, not the type value:

https://gitlab.com/CalcProgrammer1/OpenRGB/-/blob/release_0.8/RGBController/RGBController.cpp?ref_type=tags#L742

On OpenRGB next (the code that was merged after 1.0rc3 and will soon be released as 1.0), matrix maps are no longer handled as pointers, so every zone "has" a matrix map. I could update the OpenRGB side to not send empty matrix maps (w 0, h 0, map size 0) which would match the old behavior better, save bytes on the network, and reduce breakage caused by this issue. I think I will do this, but this issue should still be fixed here as it was working by happenstance as is.

septicwolf818and others added 2 commits August 18, 2026 10:57
ZoneData.pack wrote the element count in the matrix size field instead of
the byte length the protocol defines (8 + w*h*4), and struct.pack used
native alignment which inserted 2 phantom bytes after the size field,
shifting height/width and desyncing any packed matrix zone. Also crashed
on None values (0xFFFFFFFF map holes converted by unpack) and on zones
with no map (mat_height/mat_width None), breaking save_profile(local=True).
Use a packed layout with the correct byte length, map None back to
0xFFFFFFFF, and skip empty maps like the server does.
Align matrix map serialization with the OpenRGB protocol
@septicwolf818

Copy link
Copy Markdown
ContributorAuthor

@CalcProgrammer1 Yes, it's only on next (the post-1.0rc3 pipeline). In release_0.8 and earlier, matrix_map is a pointer and the block is only written when it's non-NULL, regardless of zone type - so 5-and-earlier are untouched there. On next, matrix_map became a value member (matrix_map_type matrix_map, RGBControllerInterface.h:427), and GetMatrixMapDescriptionSize (RGBController.cpp:2792) isn't gated by protocol version, so a block is now sent for every zone at any negotiated protocol version (0-5 included). That does change the wire data for the older protocols when talking to a next server - so yes, it's a next-side backwards-compat break. Skipping empty maps (size 0 when h==0 && w==0) on next would restore the old behavior. I have aligned the python parser to key off the size field like the 0.8 client, so it works with both.

@CalcProgrammer1

Copy link
Copy Markdown

Pushed a fix on the OpenRGB side to not send empty maps, but this change still should be implemented on the Python side to correctly match OpenRGB's own parser.

@jath03

Copy link
Copy Markdown
Owner

Interesting. I wasn't aware that other zone types could have a matrix map

@jath03
jath03 merged commit f2eb04b into jath03:masterAug 19, 2026
4 checks passed
@CalcProgrammer1

Copy link
Copy Markdown

Interesting. I wasn't aware that other zone types could have a matrix map

In previous versions of OpenRGB the protocol did not exclude the possibility, but I don't know of any controllers that actually did it. On 1.0, I've expanded manually configurable zones (ARGB zones) such that the user has control over the Type field. They can set a matrix map and set the Type field as two independent user options. The matrix map is saved no matter what the type field is set to which allows toggling between matrix and non matrix layouts easily. That is the main place you will see this behavior in practice now.

@CalcProgrammer1

Copy link
Copy Markdown

It also now allows device controllers to build in their own matrix map but make it optional, a keyboard could have an underglow zone that could be switched between mapping into the keyboard matrix or working as an independent light ring.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@septicwolf818@CalcProgrammer1@jath03
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1 - #93

Merged
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master
Aug 19, 2026
Merged

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1#93
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master

Conversation

@septicwolf818

Copy link
Copy Markdown
Contributor

Servers with SDK protocol >= 5 serialize a matrix map block for every zone, not just MATRIX zones. ZoneData.unpack only consumed it when zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE zones and producing garbage values (e.g. "256 is not a valid ZoneType") during device discovery.

Consume the matrix block whenever matrix_zone_size > 0, which is backward compatible since the size is 0 when no map exists. Also widen ZoneType to the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y, SEGMENTED) and add a missing fallback so future types don't crash.

septicwolf818and others added 2 commits August 11, 2026 22:57
Servers with SDK protocol >= 5 serialize a matrix map block for every
zone, not just MATRIX zones. ZoneData.unpack only consumed it when
zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE
zones and producing garbage values (e.g. "256 is not a valid ZoneType")
during device discovery.
Consume the matrix block whenever matrix_zone_size > 0, which is backward
compatible since the size is 0 when no map exists. Also widen ZoneType to
the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y,
SEGMENTED) and add a _missing_ fallback so future types don't crash.
Fix zone parsing desync on OpenRGB 1.0rc3+ servers
@CalcProgrammer1

Copy link
Copy Markdown

Is this only on pipeline? If so it's a bug and needs fixed OpenRGB side, the 5 and earlier protocols should not be changed. If backwards compatibility broke we need to fix it.

@CalcProgrammer1

Copy link
Copy Markdown

Looking at the older OpenRGB code, it actually sends matrix map only if a matrix map exists, not necessarily if type is matrix. It does so happen that 99 if not 100% of controllers that had a non-null matrix map pointer also had ZONE_TYPE_MATRIX, but the old implementation of the server didn't necessarily guarantee this relationship either so in this case I'd say the parser here is in the wrong.

For instance, in the 0.8 release's client side parser, you can see it checks the matrix map size to determine whether or not to parse a matrix map, not the type value:

https://gitlab.com/CalcProgrammer1/OpenRGB/-/blob/release_0.8/RGBController/RGBController.cpp?ref_type=tags#L742

On OpenRGB next (the code that was merged after 1.0rc3 and will soon be released as 1.0), matrix maps are no longer handled as pointers, so every zone "has" a matrix map. I could update the OpenRGB side to not send empty matrix maps (w 0, h 0, map size 0) which would match the old behavior better, save bytes on the network, and reduce breakage caused by this issue. I think I will do this, but this issue should still be fixed here as it was working by happenstance as is.

septicwolf818and others added 2 commits August 18, 2026 10:57
ZoneData.pack wrote the element count in the matrix size field instead of
the byte length the protocol defines (8 + w*h*4), and struct.pack used
native alignment which inserted 2 phantom bytes after the size field,
shifting height/width and desyncing any packed matrix zone. Also crashed
on None values (0xFFFFFFFF map holes converted by unpack) and on zones
with no map (mat_height/mat_width None), breaking save_profile(local=True).
Use a packed layout with the correct byte length, map None back to
0xFFFFFFFF, and skip empty maps like the server does.
Align matrix map serialization with the OpenRGB protocol
@septicwolf818

Copy link
Copy Markdown
ContributorAuthor

@CalcProgrammer1 Yes, it's only on next (the post-1.0rc3 pipeline). In release_0.8 and earlier, matrix_map is a pointer and the block is only written when it's non-NULL, regardless of zone type - so 5-and-earlier are untouched there. On next, matrix_map became a value member (matrix_map_type matrix_map, RGBControllerInterface.h:427), and GetMatrixMapDescriptionSize (RGBController.cpp:2792) isn't gated by protocol version, so a block is now sent for every zone at any negotiated protocol version (0-5 included). That does change the wire data for the older protocols when talking to a next server - so yes, it's a next-side backwards-compat break. Skipping empty maps (size 0 when h==0 && w==0) on next would restore the old behavior. I have aligned the python parser to key off the size field like the 0.8 client, so it works with both.

@CalcProgrammer1

Copy link
Copy Markdown

Pushed a fix on the OpenRGB side to not send empty maps, but this change still should be implemented on the Python side to correctly match OpenRGB's own parser.

@jath03

Copy link
Copy Markdown
Owner

Interesting. I wasn't aware that other zone types could have a matrix map

@jath03
jath03 merged commit f2eb04b into jath03:masterAug 19, 2026
4 checks passed
@CalcProgrammer1

Copy link
Copy Markdown

Interesting. I wasn't aware that other zone types could have a matrix map

In previous versions of OpenRGB the protocol did not exclude the possibility, but I don't know of any controllers that actually did it. On 1.0, I've expanded manually configurable zones (ARGB zones) such that the user has control over the Type field. They can set a matrix map and set the Type field as two independent user options. The matrix map is saved no matter what the type field is set to which allows toggling between matrix and non matrix layouts easily. That is the main place you will see this behavior in practice now.

@CalcProgrammer1

Copy link
Copy Markdown

It also now allows device controllers to build in their own matrix map but make it optional, a keyboard could have an underglow zone that could be switched between mapping into the keyboard matrix or working as an independent light ring.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@septicwolf818@CalcProgrammer1@jath03
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1 - #93

Merged
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master
Aug 19, 2026
Merged

Fix zone parsing desync on OpenRGB 1.0rc3+ servers- #1#93
jath03 merged 6 commits into
jath03:masterfrom
septicwolf818:master

Conversation

@septicwolf818

Copy link
Copy Markdown
Contributor

Servers with SDK protocol >= 5 serialize a matrix map block for every zone, not just MATRIX zones. ZoneData.unpack only consumed it when zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE zones and producing garbage values (e.g. "256 is not a valid ZoneType") during device discovery.

Consume the matrix block whenever matrix_zone_size > 0, which is backward compatible since the size is 0 when no map exists. Also widen ZoneType to the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y, SEGMENTED) and add a missing fallback so future types don't crash.

septicwolf818and others added 2 commits August 11, 2026 22:57
Servers with SDK protocol >= 5 serialize a matrix map block for every
zone, not just MATRIX zones. ZoneData.unpack only consumed it when
zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE
zones and producing garbage values (e.g. "256 is not a valid ZoneType")
during device discovery.
Consume the matrix block whenever matrix_zone_size > 0, which is backward
compatible since the size is 0 when no map exists. Also widen ZoneType to
the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y,
SEGMENTED) and add a _missing_ fallback so future types don't crash.
Fix zone parsing desync on OpenRGB 1.0rc3+ servers
@CalcProgrammer1

Copy link
Copy Markdown

Is this only on pipeline? If so it's a bug and needs fixed OpenRGB side, the 5 and earlier protocols should not be changed. If backwards compatibility broke we need to fix it.

@CalcProgrammer1

Copy link
Copy Markdown

Looking at the older OpenRGB code, it actually sends matrix map only if a matrix map exists, not necessarily if type is matrix. It does so happen that 99 if not 100% of controllers that had a non-null matrix map pointer also had ZONE_TYPE_MATRIX, but the old implementation of the server didn't necessarily guarantee this relationship either so in this case I'd say the parser here is in the wrong.

For instance, in the 0.8 release's client side parser, you can see it checks the matrix map size to determine whether or not to parse a matrix map, not the type value:

https://gitlab.com/CalcProgrammer1/OpenRGB/-/blob/release_0.8/RGBController/RGBController.cpp?ref_type=tags#L742

On OpenRGB next (the code that was merged after 1.0rc3 and will soon be released as 1.0), matrix maps are no longer handled as pointers, so every zone "has" a matrix map. I could update the OpenRGB side to not send empty matrix maps (w 0, h 0, map size 0) which would match the old behavior better, save bytes on the network, and reduce breakage caused by this issue. I think I will do this, but this issue should still be fixed here as it was working by happenstance as is.

septicwolf818and others added 2 commits August 18, 2026 10:57
ZoneData.pack wrote the element count in the matrix size field instead of
the byte length the protocol defines (8 + w*h*4), and struct.pack used
native alignment which inserted 2 phantom bytes after the size field,
shifting height/width and desyncing any packed matrix zone. Also crashed
on None values (0xFFFFFFFF map holes converted by unpack) and on zones
with no map (mat_height/mat_width None), breaking save_profile(local=True).
Use a packed layout with the correct byte length, map None back to
0xFFFFFFFF, and skip empty maps like the server does.
Align matrix map serialization with the OpenRGB protocol
@septicwolf818

Copy link
Copy Markdown
ContributorAuthor

@CalcProgrammer1 Yes, it's only on next (the post-1.0rc3 pipeline). In release_0.8 and earlier, matrix_map is a pointer and the block is only written when it's non-NULL, regardless of zone type - so 5-and-earlier are untouched there. On next, matrix_map became a value member (matrix_map_type matrix_map, RGBControllerInterface.h:427), and GetMatrixMapDescriptionSize (RGBController.cpp:2792) isn't gated by protocol version, so a block is now sent for every zone at any negotiated protocol version (0-5 included). That does change the wire data for the older protocols when talking to a next server - so yes, it's a next-side backwards-compat break. Skipping empty maps (size 0 when h==0 && w==0) on next would restore the old behavior. I have aligned the python parser to key off the size field like the 0.8 client, so it works with both.

@CalcProgrammer1

Copy link
Copy Markdown

Pushed a fix on the OpenRGB side to not send empty maps, but this change still should be implemented on the Python side to correctly match OpenRGB's own parser.

@jath03

Copy link
Copy Markdown
Owner

Interesting. I wasn't aware that other zone types could have a matrix map

@jath03
jath03 merged commit f2eb04b into jath03:masterAug 19, 2026
4 checks passed
@CalcProgrammer1

Copy link
Copy Markdown

Interesting. I wasn't aware that other zone types could have a matrix map

In previous versions of OpenRGB the protocol did not exclude the possibility, but I don't know of any controllers that actually did it. On 1.0, I've expanded manually configurable zones (ARGB zones) such that the user has control over the Type field. They can set a matrix map and set the Type field as two independent user options. The matrix map is saved no matter what the type field is set to which allows toggling between matrix and non matrix layouts easily. That is the main place you will see this behavior in practice now.

@CalcProgrammer1

Copy link
Copy Markdown

It also now allows device controllers to build in their own matrix map but make it optional, a keyboard could have an underglow zone that could be switched between mapping into the keyboard matrix or working as an independent light ring.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@septicwolf818@CalcProgrammer1@jath03