Repository: wezterm/wezterm (language: rust)

Issue:
Sixel parser ignores `P2` parameter if `P1` is blank
### What Operating System(s) are you seeing this problem on?

Linux Wayland

### Which Wayland compositor or X11 Window manager(s) are you using?

kwin

### WezTerm version

20231228-084719-ff274374

### Did you try the latest nightly build to see if the issue is better (or worse!) than your current version?

Yes, and I updated the version box above to show the version of the nightly that I tried

### Describe the bug

A sixel sequence follows the pattern:

```
DCS  P1;  P2;  P3;  q  s..s  ST
```

where `P1` is the macro parameter and `P2` selects how the terminal draws the background color.

If `P1` is blank but `P2` is set to `1` as follows:

```
\x1bP;1q ...
```

then unspecified pixel are set to the current background color instead of remaining at their current color as expected.

### To Reproduce

Run the following twice in the terminal:

```bash
echo -e '\eP;1q;#1;2;100;0;0#1??}}GG}}??}}??--#0;2;0;100;100\e\\'
```

![image](https://github.com/wez/wezterm/assets/12154190/77f4ed8f-3a57-4afe-90d1-b80bb21deb2d)

The second sixel graphic has a cyan background, which is not expected: it should be transparent, because `P2` was set to `1`. 

The third sixel graphic shows the expected output (`P1` is not blank and `P2` is `1`, resulting in transparent background).

### Configuration

default

### Expected Behavior

I would expect sixel device control string parameters to be parsed even if preceding parameters are blank.

### Logs

_No response_

### Anything else?

_No response_
Sixel parser ignores `P2` parameter if `P1` is blank
### What Operating System(s) are you seeing this problem on?

Linux Wayland

### Which Wayland compositor or X11 Window manager(s) are you using?

kwin

### WezTerm version

20231228-084719-ff274374

### Did you try the latest nightly build to see if the issue is better (or worse!) than your current version?

Yes, and I updated the version box above to show the version of the nightly that I tried

### Describe the bug

A sixel sequence follows the pattern:

```
DCS  P1;  P2;  P3;  q  s..s  ST
```

where `P1` is the macro parameter and `P2` selects how the terminal draws the background color.

If `P1` is blank but `P2` is set to `1` as follows:

```
\x1bP;1q ...
```

then unspecified pixel are set to the current background color instead of remaining at their current color as expected.

### To Reproduce

Run the following twice in the terminal:

```bash
echo -e '\eP;1q;#1;2;100;0;0#1??}}GG}}??}}??--#0;2;0;100;100\e\\'
```

![image](https://github.com/wez/wezterm/assets/12154190/77f4ed8f-3a57-4afe-90d1-b80bb21deb2d)

The second sixel graphic has a cyan background, which is not expected: it should be transparent, because `P2` was set to `1`. 

The third sixel graphic shows the expected output (`P1` is not blank and `P2` is `1`, resulting in transparent background).

### Configuration

default

### Expected Behavior

I would expect sixel device control string parameters to be parsed even if preceding parameters are blank.

### Logs

_No response_

### Anything else?

_No response_


Discussion / hints:
It appears that the sixel parameter parser ignores any leading parameter deliminators.

I would not expect the following to render an image with a transparent background:

![image](https://github.com/wez/wezterm/assets/12154190/c1455489-791d-4957-97c2-3c49c080f225)

It looks like omitted parameters get added at the end of the parameter array:

![image](https://github.com/wez/wezterm/assets/12154190/923e7684-dbb0-46b3-81e4-b3d083b1596c)

I think perhaps there is a bug in `VTParser.as_integer_params`?

Presumably `i` should be incremented whether the parameter is a `CsiParam::Integer` or not?

```diff
    fn as_integer_params(&self) -> [i64; MAX_PARAMS] {
        let mut res = [0i64; MAX_PARAMS];
        let mut i = 0;
        for src in &self.params[0..self.num_params] {
            if let CsiParam::Integer(value) = src {
                res[i] = *value;
-               i += 1;
            }
+           i += 1;
        }
        res
    }
```
It appears that the sixel parameter parser ignores any leading parameter deliminators.

I would not expect the following to render an image with a transparent background:

![image](https://github.com/wez/wezterm/assets/12154190/c1455489-791d-4957-97c2-3c49c080f225)

It looks like omitted parameters get added at the end of the parameter array:

![image](https://github.com/wez/wezterm/assets/12154190/923e7684-dbb0-46b3-81e4-b3d083b1596c)

I think perhaps there is a bug in `VTParser.as_integer_params`?

Presumably `i` should be incremented whether the parameter is a `CsiParam::Integer` or not?

```diff
    fn as_integer_params(&self) -> [i64; MAX_PARAMS] {
        let mut res = [0i64; MAX_PARAMS];
        let mut i = 0;
        for src in &self.params[0..self.num_params] {
            if let CsiParam::Integer(value) = src {
                res[i] = *value;
-               i += 1;
            }
+           i += 1;
        }
        res
    }
```

Fix patch:
diff --git a/vtparse/src/lib.rs b/vtparse/src/lib.rs
index c7b83da98a8..3eda73853f5 100644
--- a/vtparse/src/lib.rs
+++ b/vtparse/src/lib.rs
@@ -444,6 +444,7 @@ impl VTParser {
         for src in &self.params[0..self.num_params] {
             if let CsiParam::Integer(value) = src {
                 res[i] = *value;
+            } else if let CsiParam::P(b';') = src {
                 i += 1;
             }
         }
@@ -1103,4 +1104,26 @@ mod test {
             ]
         );
     }
+
+    #[test]
+    fn test_ommitted_dcs_param() {
+        assert_eq!(
+            parse_as_vec("\x1bP;1q\x1b\\".as_bytes()),
+            vec![
+                VTAction::DcsHook {
+                    byte: b'q',
+                    params: vec![0, 1],
+                    intermediates: vec![],
+                    ignored_excess_intermediates: false,
+                },
+                VTAction::DcsUnhook,
+                VTAction::EscDispatch {
+                    params: vec![],
+                    intermediates: vec![],
+                    ignored_excess_intermediates: false,
+                    byte: b'\\',
+                }
+            ]
+        );
+    }
 }


Test patch: