Search code examples
cparsingxlibx-dwm

Parse string for coloring text in Xlib


I am trying to write a patch for DWM 6.1 to color the status bar, so far I have managed to do so, but I am using hexadecimal characters like \x01 to represent the colors. And doing so I have found a problem. When dwm gets the width of the status bar, is counting with the escape characters as well, resulting in a misplaced status bar.

I've write a dummy program to try to solve this. My idea is, given the following status text:

char buf[] = "Hello World\x01, Bye!\x02";

That must write Hello World in color 1 and , Bye! in color 2. I want to parse the text and get:

char cleanBuf[] = "Hello World, Bye!";

I've already done that. But now I need in some way remember where the escape sequences where in order to draw the text with the corresponding color. I thought having a stack storing the colors in the order they appears, in this case the stack would have {color1, color2}. And a structure with the start and end pointers for the text that must be in a color, for Hello World I would need a pointer to H and another for d. This way I could create a new string to parse to XDrawString and printed it with the corresponding color.

Is this the best way of doing it? I think I am complicating the things a bit. If you want to try, the complete program is here. I paste here the main loop:

while (1) {
  XNextEvent(dpy, &e);

  if (e.type == Expose && e.xexpose.count < 1) {

    char buf[] = "Hello World\x01, Bye!\x02";
    char cleanBuf[strlen(buf)];
    memset(cleanBuf, 0, strlen(cleanBuf));
    char *copy = strdup(buf);
    char *delim = "\x01\x02";
    char *res = strtok(buf, delim);
    strcat(cleanBuf, res);
    unsigned long color1 = 0xff0000;
    unsigned long color2 = 0x00ff00;
    unsigned long color;
    int x = 10;
    while (res) {
      /* Figure out what delimiter was used */
      // Thanks to http://stackoverflow.com/a/12460511/1612432
      char deli = copy[res - buf + strlen(res)];
      if (deli == '\x01')
        color = color1;
      else if (deli == '\x02')
        color = color2;
      else
        color = 0xffffff;
      XSetForeground(dpy, gc, color);
      XDrawString(dpy, win, gc, x, 10, res, strlen(res));
      x += 50;
      res = strtok(0, delim);
      if (res)
        strcat(cleanBuf, res);
    }
    free(copy);

  } else if (e.type == ButtonPress)
    break;

}

Solution

  • I finally could parse the string with the following method:

    void
    parsestatus(char *text, unsigned long *color_queue, char tokens[][256]) {
    
      // TODO move variables that can to main in order to not recreated them
      char *copy = strdup(text);
      char cleanBuf[strlen(text)];
      memset(cleanBuf, 0, strlen(cleanBuf));
    
      char delim[NUMCOLORS+1];
    
      /* Thanks to http://stackoverflow.com/a/24931903/1612432 */
      for (int i = 0; i < NUMCOLORS; ++i)
          delim[i] = i + 1;
      /* Terminates as string */
      delim[NUMCOLORS] = '\0';
    
      char *res = strtok(copy, delim);
      strcat(tokens[0], res);
      strcat(cleanBuf, res);
      int i = 1;
    
      while (res) {
        /* Figure out what delimiter was used */
        // Thanks to http://stackoverflow.com/a/12460511/1612432
        int deli = text[res - copy + strlen(res)] - 1;
        color_queue[i-1] = colors[deli];
        res = strtok(0, delim);
        if (res){
          strcat(tokens[i++], res);
          strcat(cleanBuf, res);
        }
      }
      free(copy);
      strncpy(text, cleanBuf, strlen(cleanBuf));
      text[strlen(cleanBuf)] = '\0';
    }
    

    And then , after every call to XmbDrawString I subtract the length of the termination character like this:

    tx += TEXTW(text[k]) - TEXTW("\x0");