├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── examples ├── test.sh └── wifi-signal ├── mktrayicon.c └── package └── arch ├── .gitignore └── PKGBUILD /.gitignore: -------------------------------------------------------------------------------- 1 | # Binary 2 | mktrayicon 3 | 4 | # Object files 5 | *.o 6 | 7 | # Libraries 8 | *.lib 9 | *.a 10 | 11 | # Shared objects (inc. Windows DLLs) 12 | *.dll 13 | *.so 14 | *.so.* 15 | *.dylib 16 | 17 | # Executables 18 | *.exe 19 | *.out 20 | *.app 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jon Gjengset 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | mktrayicon: mktrayicon.c 2 | ${CC} `pkg-config --cflags gtk+-3.0` -o $@ $< `pkg-config --libs gtk+-3.0` `pkg-config --cflags --libs x11` 3 | 4 | clean: 5 | rm mktrayicon 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mktrayicon 2 | 3 | `mktrayicon` is a simple proxy program that lets you create and modify 4 | system tray icons without having to deal with a graphical toolkit like 5 | GTK. 6 | 7 | `mktrayicon` can be used two ways: To create an icon that is controlled 8 | by a named pipe or, more simply, to create a non-interactive icon. 9 | 10 | If a FIFO is not provided, mktrayicon will run until killed (e.g., 11 | `pkill -f 'mktrayicon.*'`). If you are using a named pipe (FIFO) 12 | to control the icon, *the pipe should already be created before you call 13 | `mktrayicon`*. 14 | 15 | Every line written to the pipe should contain a single letter specifying 16 | what operation to perform, optionally followed by a space and a 17 | parameter to the command. Each command should be terminated by a 18 | newline. The following commands are supported: 19 | 20 | - `q`: Terminate `mktrayicon` and remove the tray icon 21 | - `i `: Set the graphic to use for the tray icon; it can be a 22 | stock icon name (see `/usr/share/icons`) or path to a custom icon 23 | - `t `: Set the text to display in the icon tooltip 24 | - `t`: Remove the icon tooltip 25 | - `c `: Set the command to be execute when the user clicks the 26 | icon (`cmnd` is passed to `/bin/sh -c`) 27 | - `c`: Remove the click handler 28 | - `m ,|,|...`: Set the labels and the 29 | corresponding commands to be executed when the user opens the icon 30 | menu (right-click usually) (`cmd#` is passed to `/bin/sh -c`) 31 | - `m`: Remove the menu handler 32 | - `h`: Hide the tray icon 33 | - `s`: Show the tray icon 34 | 35 | By default, the `none` tooltip icon is used. To change this, pass `-i 36 | ` or `-i ` when running 37 | `mktrayicon`. 38 | 39 | Note that any script communicating with `mktrayicon` via the pipe 40 | **must**, for the time being, send `q` when they are done. Just removing 41 | the FIFO file will **not** cause the tray icon to be removed. 42 | 43 | The command argument can be quoted with either `'` or `"` if you wish it 44 | to include newlines. Other string interpolation may be added later. 45 | Quoted strings are terminated by a matching quote at the end of a line 46 | (ignoring whitespace). To escape a quote character at the end of a line 47 | to continue a quoted string, prefix it with a `\`. 48 | 49 | The m(enu) command uses `,` as a delimiter between label and command and 50 | `|` as a delimiter between entries (label+command). If you want to use 51 | these two characters in a label or command, you have to escape them with 52 | `\`. You can make a blank label or a label without an action by leaving 53 | out the `label` or `cmd` respectively. For example: 54 | 55 | ```console 56 | $ echo "m Browser,firefox|Terminal,xterm|Label-only||,chromium" > /tmp/test 57 | $ # (where `mkfifo /tmp/test` has been executed before) 58 | ``` 59 | 60 | Would give you a menu with five entries: 61 | 62 | - "Browser", which launches `firefox` when clicked 63 | - "Terminal", which launches `xterm` when clicked 64 | - "Label-only", which does nothing if clicked 65 | - An unlabeled, inactive entry (useful as a separator) 66 | - An unlabeled entry which launches `chromium` when clicked 67 | 68 | ## Why? 69 | 70 | Because I wanted to be able to create tray icons from bash without all 71 | the hassle of interacting with GTK. Now I can create scripts for 72 | measuring stuff and instantly make tray icons out of them (3G signal 73 | strength for example). 74 | 75 | ## Example run 76 | 77 | This example is also in `examples/test.sh` so you can try running it. 78 | 79 | ```bash 80 | #!/bin/bash 81 | 82 | # Set up tray icon 83 | mkfifo /tmp/$$.icon 84 | ./mktrayicon /tmp/$$.icon & 85 | 86 | # Manipulate tray icon 87 | 88 | # Click handling 89 | echo "c xterm -e /bin/sh -c 'iwconfig; read'" > /tmp/$$.icon 90 | 91 | # Change the icon and tooltip 92 | for i in none weak ok good excellent; do 93 | echo "i network-wireless-signal-$i-symbolic" > /tmp/$$.icon 94 | echo "t Signal strength: $i" > /tmp/$$.icon 95 | sleep 2 96 | done 97 | 98 | # Remove tooltip and click handler 99 | echo "c" > /tmp/$$.icon 100 | echo "t" > /tmp/$$.icon 101 | 102 | # Toggle the visibility of the icon for a bit 103 | for i in {1..3}; do 104 | for j in h s; do 105 | echo $j > /tmp/$$.icon 106 | sleep 1 107 | done 108 | done 109 | 110 | # Remove tray icon 111 | echo "q" > /tmp/$$.icon 112 | rm /tmp/$$.icon 113 | ``` 114 | 115 | ## Known bugs 116 | 117 | This is my first time using the GTK+ C library, and I've got to say it 118 | is less than pleasant to work with. My biggest issue has been trying to 119 | do blocking IO without blocking the GUI thread, as GTK seems to not like 120 | that. 121 | 122 | **Patches are very welcome!** 123 | -------------------------------------------------------------------------------- /examples/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Set up tray icon 4 | mkfifo /tmp/$$.icon 5 | ./mktrayicon /tmp/$$.icon & 6 | 7 | # Manipulate tray icon 8 | 9 | # Click handling 10 | echo "c xterm -e /bin/sh -c 'iwconfig; read'" > /tmp/$$.icon 11 | 12 | # Change the icon and tooltip 13 | for i in none weak ok good excellent; do 14 | echo "i network-wireless-signal-$i-symbolic" > /tmp/$$.icon 15 | echo "t Signal strength: $i" > /tmp/$$.icon 16 | sleep 1 17 | done 18 | 19 | # Remove tooltip and click handler 20 | echo "c" > /tmp/$$.icon 21 | echo "t" > /tmp/$$.icon 22 | 23 | # Toggle the visibility of the icon for a bit 24 | for i in {1..3}; do 25 | for j in h s; do 26 | echo $j > /tmp/$$.icon 27 | sleep .5s 28 | done 29 | done 30 | 31 | # Remove tray icon 32 | echo "q" > /tmp/$$.icon 33 | rm /tmp/$$.icon 34 | -------------------------------------------------------------------------------- /examples/wifi-signal: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | wifi="/tmp/$$-wifi.icon" 3 | 4 | # Set up tray icon 5 | mkfifo $wifi 6 | mktrayicon $wifi 2>/dev/null & 7 | echo "h" > $wifi 8 | run=1 9 | 10 | trap 'run=0' INT; 11 | while [[ $run -eq 1 ]]; do 12 | # See network-manager:wifi:wext_qual_to_percent 13 | # http://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/src/wifi/wifi-utils-wext.c#n264 14 | qual=`iwconfig 2>/dev/null | grep "Link Quality" | sed 's/.*Link Quality=\([0-9]*\/[0-9]*\) .*/\1/'` 15 | if [[ -n $qual ]]; then 16 | qual=`echo "100*$qual" | bc` 17 | if ((qual<25)); then 18 | echo "i network-wireless-signal-weak-symbolic" > $wifi; 19 | echo "t Weak ($qual%)" > $wifi; 20 | elif ((qual<50)); then 21 | echo "i network-wireless-signal-ok-symbolic" > $wifi; 22 | echo "t OK ($qual%)" > $wifi; 23 | elif ((qual<75)); then 24 | echo "i network-wireless-signal-good-symbolic" > $wifi; 25 | echo "t Good ($qual%)" > $wifi; 26 | else 27 | echo "i network-wireless-signal-excellent-symbolic" > $wifi; 28 | echo "t Excellent ($qual%)" > $wifi; 29 | fi 30 | 31 | echo "s" > $wifi; 32 | else 33 | echo "h" > $wifi; 34 | fi 35 | 36 | sleep 30 37 | done 38 | 39 | # Remove tray icon 40 | echo "q" > $wifi 41 | rm $wifi 42 | -------------------------------------------------------------------------------- /mktrayicon.c: -------------------------------------------------------------------------------- 1 | /* 2 | * @author Jon Gjengset 3 | * @see http://blog.sacaluta.com/2007/08/gtk-system-tray-icon-example.html 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | /* 17 | * This function is made because it's needed to escape the '\' character 18 | * The basic code has been taken from the man pages of the strncpy function 19 | */ 20 | char *strncpy_esc(char *dest, const char *src, size_t n) { 21 | size_t i = 0; 22 | size_t index = 0; 23 | while (i < n && src[i] != '\0') { 24 | if (src[i] == '\\' && src[i + 1] != '\0') { 25 | dest[index] = src[i + 1]; 26 | index++; 27 | i += 2; 28 | } else { 29 | dest[index] = src[i]; 30 | index++; 31 | i++; 32 | } 33 | } 34 | for (; index < n; index++) { 35 | dest[index] = '\0'; 36 | } 37 | return dest; 38 | } 39 | 40 | char *save_word(char *src, int i_del, int last) { 41 | char *dest = malloc((i_del - last) * sizeof(char)); 42 | strncpy_esc(dest, src + last + 1, i_del - last - 1); 43 | dest[i_del - last - 1] = '\0'; 44 | return dest; 45 | } 46 | 47 | /* 48 | * Struct that stores the label names on the menu and 49 | * their corresponding actions when the user selects them 50 | */ 51 | struct item { 52 | char *name; 53 | char *action; 54 | } * onmenu; 55 | int menusize = 0; // number of menu entries 56 | GtkWidget *menu = NULL; 57 | 58 | GtkStatusIcon *icon; 59 | char *onclick = NULL; 60 | 61 | void tray_icon_on_click(GtkStatusIcon *status_icon, gpointer user_data) { 62 | if (onclick != NULL && fork() == 0) { 63 | execl("/bin/sh", "sh", "-c", onclick, (char *)NULL); 64 | } 65 | } 66 | 67 | /* 68 | * Callback function for when an entry is selected from the menu 69 | * We loop over all entry names to find what action to execute 70 | */ 71 | void click_menu_item(GtkMenuItem *menuitem, gpointer user_data) { 72 | const char *label = gtk_menu_item_get_label(menuitem); 73 | for (int i = 0; i < menusize; i++) { 74 | if (strcmp(label, onmenu[i].name) == 0 && fork() == 0) { 75 | execl("/bin/sh", "sh", "-c", onmenu[i].action, (char *)NULL); 76 | } 77 | } 78 | } 79 | 80 | void tray_icon_on_menu(GtkStatusIcon *status_icon, guint button, 81 | guint activate_time, gpointer user_data) { 82 | #ifdef DEBUG 83 | printf("Popup menu\n"); 84 | #endif 85 | if (menusize) { 86 | gtk_menu_popup_at_pointer((GtkMenu *)menu, NULL); 87 | } 88 | } 89 | 90 | gboolean set_tooltip(gpointer data) { 91 | char *p = (char *)data; 92 | if (*p == '\0') { 93 | #ifdef DEBUG 94 | printf("Removing tooltip\n"); 95 | #endif 96 | gtk_status_icon_set_has_tooltip(icon, FALSE); 97 | return FALSE; 98 | } 99 | 100 | #ifdef DEBUG 101 | printf("Setting tooltip to '%s'\n", p); 102 | #endif 103 | gtk_status_icon_set_tooltip_text(icon, p); 104 | free(data); 105 | return FALSE; 106 | } 107 | 108 | gboolean set_icon(gpointer data) { 109 | char *p = (char *)data; 110 | #ifdef DEBUG 111 | printf("Setting icon to '%s'\n", p); 112 | #endif 113 | if (strchr(p, '/')) { 114 | gtk_status_icon_set_from_file(icon, p); 115 | } else { 116 | gtk_status_icon_set_from_icon_name(icon, p); 117 | } 118 | free(data); 119 | return FALSE; 120 | } 121 | 122 | gboolean set_visible(gpointer data) { 123 | gtk_status_icon_set_visible(icon, data == 0 ? FALSE : TRUE); 124 | return FALSE; 125 | } 126 | 127 | gboolean do_quit(gpointer data) { 128 | gtk_main_quit(); 129 | return FALSE; 130 | } 131 | 132 | gpointer watch_fifo(gpointer argv) { 133 | char *buf = malloc(1024 * sizeof(char)); 134 | char cmd; 135 | char quote; 136 | char *param; 137 | char *tmp = malloc(1024 * sizeof(char)); 138 | char *read; 139 | size_t len, i; 140 | char *fifo_path = (char *)argv; 141 | FILE *fifo; 142 | struct stat fifo_st; 143 | 144 | /* outer is for open */ 145 | outer: 146 | while (1) { 147 | if (stat(fifo_path, &fifo_st) != 0) { 148 | perror("FIFO does not exist, exiting\n"); 149 | gdk_threads_add_idle(do_quit, fifo); 150 | return NULL; 151 | } 152 | 153 | fifo = fopen(fifo_path, "r"); 154 | 155 | if (fifo == NULL) { 156 | perror("FIFO went away, exiting\n"); 157 | gdk_threads_add_idle(do_quit, fifo); 158 | return NULL; 159 | } 160 | 161 | /* inner is for read */ 162 | while (1) { 163 | read = fgets(buf, 1024 * sizeof(char), fifo); 164 | 165 | if (read == NULL) { 166 | /* no more data in pipe, reopen and block */ 167 | fclose(fifo); 168 | goto outer; 169 | } 170 | 171 | /* trim string */ 172 | while ((*read == '\n' || *read == ' ' || *read == '\t') && 173 | *read != '\0') { 174 | read++; 175 | } 176 | 177 | if (*read == '\0') { 178 | /* empty command */ 179 | continue; 180 | } 181 | 182 | cmd = *read; 183 | len = strlen(read); 184 | if (len < 3) { 185 | param = NULL; 186 | } else if (*(read + 2) != '\'' && *(read + 2) != '"') { 187 | // unquoted string 188 | read += 2; 189 | len -= 2; 190 | // trim trailing whitespace 191 | i = len - 1; 192 | while (i > 0) { 193 | if (!isspace(read[i])) { 194 | len = i + 1; 195 | read[len] = '\0'; 196 | break; 197 | } 198 | i -= 1; 199 | } 200 | param = malloc((len + 1) * sizeof(char)); 201 | strncpy(param, read, len + 1); 202 | } else { 203 | // quoted string 204 | quote = *(read + 2); 205 | read += 3; 206 | len -= 3; 207 | *tmp = '\0'; 208 | *(tmp + 1024 - 1) = '\0'; 209 | // keep track of what we have so far 210 | strncpy(tmp, read, 1023); 211 | 212 | // now keep reading until we have the end quote 213 | while (1) { 214 | // check for terminating ' 215 | if (len != 0) { 216 | // search backwards past whitespace 217 | i = len - 1; 218 | while (i > 0) { 219 | if (!isspace(tmp[i])) { 220 | break; 221 | } 222 | i -= 1; 223 | } 224 | if (tmp[i] == quote) { 225 | // maybe the end! 226 | // let's make sure it isn't escaped 227 | if (i >= 2 && tmp[i - 2] == '\\') { 228 | } else { 229 | // it's not! 230 | // we're done. 231 | // trim off the ' and 232 | // any whitespace we walked past 233 | len = i; 234 | tmp[len] = '\0'; 235 | break; 236 | } 237 | } 238 | } 239 | 240 | if (len == 1023) { 241 | // we can't read any more 242 | // but also haven't found the end 243 | // forcibly terminate the string 244 | fprintf(stderr, "Quoted string too long (max 1023 chars)\n"); 245 | break; 246 | } 247 | 248 | // we don't have the end of the string yet 249 | read = fgets(buf, 1024 * sizeof(char), fifo); 250 | if (read == NULL) { 251 | /* no more data in pipe, reopen and block */ 252 | fclose(fifo); 253 | goto outer; 254 | } 255 | // note that we don't trim here, because we're 256 | // in a quoted string. 257 | strncpy(tmp + len, read, 1023 - len); 258 | len += strlen(tmp + len); 259 | } 260 | 261 | // quoted string is now in param[0:len] 262 | param = malloc((len + 1) * sizeof(char)); 263 | strncpy(param, tmp, len + 1); 264 | } 265 | 266 | switch (cmd) { 267 | case 'q': 268 | gdk_threads_add_idle(do_quit, param); 269 | if (param != NULL) { 270 | free(param); 271 | } 272 | break; 273 | case 't': /* tooltip */ 274 | gdk_threads_add_idle(set_tooltip, param); 275 | break; 276 | case 'i': /* icon */ 277 | gdk_threads_add_idle(set_icon, param); 278 | break; 279 | case 'h': /* hide */ 280 | gdk_threads_add_idle(set_visible, (void *)0); 281 | if (param != NULL) { 282 | free(param); 283 | } 284 | break; 285 | case 's': /* show */ 286 | gdk_threads_add_idle(set_visible, (void *)1); 287 | if (param != NULL) { 288 | free(param); 289 | } 290 | break; 291 | case 'c': /* click */ 292 | if (onclick != NULL) { 293 | free(onclick); 294 | onclick = NULL; 295 | } 296 | 297 | if (param != NULL && *param == '\0') { 298 | #ifdef DEBUG 299 | printf("Removing onclick handler\n"); 300 | #endif 301 | free(param); 302 | break; 303 | } 304 | 305 | onclick = param; 306 | #ifdef DEBUG 307 | printf("Setting onclick handler to '%s'\n", onclick); 308 | #endif 309 | break; 310 | case 'm': /* menu */ 311 | if (onmenu != NULL) { 312 | // destroy the previous menu 313 | for (int i = 0; i < menusize; i++) { 314 | free(onmenu[i].name); 315 | free(onmenu[i].action); 316 | onmenu[i].name = NULL; 317 | onmenu[i].action = NULL; 318 | } 319 | free(onmenu); 320 | onmenu = NULL; 321 | gtk_widget_destroy(menu); 322 | menu = NULL; 323 | } 324 | 325 | menusize = 0; 326 | 327 | if (!param) { 328 | break; 329 | } else if (*param == '\0') { 330 | #ifdef DEBUG 331 | printf("Removing onmenu handler\n"); 332 | #endif 333 | free(param); 334 | break; 335 | } 336 | 337 | // This block makes sure that the parameter after 'm' is ready to be 338 | // processed We can't accept 2 straight commas, as it becomes ambiguous 339 | int straight = 0; 340 | int bars = 0; 341 | for (int i = 0; i < len; i++) { 342 | if (param[i] == ',' && param[i - 1] != '\\') { 343 | straight++; 344 | if (straight == 2) { 345 | break; 346 | } 347 | } else if (param[i] == '|' && param[i - 1] != '\\') { 348 | straight = 0; 349 | bars++; 350 | } 351 | } 352 | if (straight == 2) { 353 | printf("Two straight ',' found. Use '\\' to escape\n"); 354 | free(param); 355 | break; 356 | } 357 | // End of block that checks the parameter 358 | 359 | // Create the onmenu array which stores structs with name, action 360 | // properties 361 | menusize = bars + 1; 362 | onmenu = malloc(menusize * sizeof(struct item)); 363 | menu = gtk_menu_new(); 364 | int last = -1; 365 | int item = 0; 366 | char lastFound = '|'; // what was the last delimiter processed 367 | for (int i = 0; i < len; i++) { 368 | if (param[i] == ',' && param[i - 1] != '\\') { 369 | onmenu[item].name = save_word(param, i, last); 370 | last = i; 371 | lastFound = ','; 372 | } else if (param[i] == '|' && param[i - 1] != '\\') { 373 | if (lastFound == ',') { // we have found a ',' so we read an action 374 | onmenu[item].action = save_word(param, i, last); 375 | } else { // this is a label-only entry 376 | onmenu[item].name = save_word(param, i, last); 377 | onmenu[item].action = malloc(1); // pointer has to be freeable 378 | *onmenu[item].action = '\0'; 379 | } 380 | last = i; 381 | lastFound = '|'; 382 | item++; 383 | } 384 | } 385 | if (item < menusize) { // haven't read all actions because last one 386 | // didn't end with a '|' 387 | if (lastFound == ',') { 388 | onmenu[item].action = save_word(param, len, last); 389 | } else { 390 | onmenu[item].name = save_word(param, len, last); 391 | onmenu[item].action = malloc(1); 392 | *onmenu[item].action = '\0'; 393 | } 394 | } 395 | 396 | // Now create the menu item widgets and attach them on the menu 397 | for (int i = 0; i < menusize; i++) { 398 | GtkWidget *w = gtk_menu_item_new_with_label(onmenu[i].name); 399 | gtk_menu_shell_append(GTK_MENU_SHELL(menu), w); 400 | g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(click_menu_item), 401 | NULL); 402 | } 403 | gtk_widget_show_all(menu); 404 | free(param); 405 | break; 406 | default: 407 | fprintf(stderr, "Unknown command: '%c'\n", *buf); 408 | if (param != NULL) { 409 | free(param); 410 | } 411 | } 412 | 413 | gdk_flush(); 414 | } 415 | } 416 | return NULL; 417 | } 418 | 419 | static GtkStatusIcon *create_tray_icon(char *start_icon) { 420 | GtkStatusIcon *tray_icon; 421 | 422 | if (strchr(start_icon, '/')) { 423 | tray_icon = gtk_status_icon_new_from_file(start_icon); 424 | } else { 425 | tray_icon = gtk_status_icon_new_from_icon_name(start_icon); 426 | } 427 | g_signal_connect(G_OBJECT(tray_icon), "activate", 428 | G_CALLBACK(tray_icon_on_click), NULL); 429 | g_signal_connect(G_OBJECT(tray_icon), "popup-menu", 430 | G_CALLBACK(tray_icon_on_menu), NULL); 431 | gtk_status_icon_set_visible(tray_icon, TRUE); 432 | 433 | return tray_icon; 434 | } 435 | 436 | int main(int argc, char **argv) { 437 | char *start_icon = "none"; 438 | char *tooltip = NULL; 439 | char *pipe = NULL; 440 | GThread *reader; 441 | 442 | XInitThreads(); /* see http://stackoverflow.com/a/18690540/472927 */ 443 | gtk_init(&argc, &argv); 444 | 445 | if (argc == 1) { 446 | printf("Usage: %s [-i ICON] [-t TOOLTIP] [FIFO]\n", *argv); 447 | printf("Create a system tray icon as specified\n"); 448 | printf("\n"); 449 | printf(" -i ICON\tUse the specified ICON when initializing\n"); 450 | printf(" -t TOOLTIP\tUse the specified TOOLTIP when initializing\n"); 451 | printf("\n"); 452 | printf("If a FIFO is not provided, mktrayicon will run until killed\n"); 453 | printf("Report bugs at https://github.com/jonhoo/mktrayicon\n"); 454 | return 0; 455 | } 456 | 457 | int c; 458 | while ((c = getopt(argc, argv, "i:t:")) != -1) 459 | switch (c) { 460 | case 'i': 461 | start_icon = optarg; 462 | break; 463 | case 't': 464 | tooltip = optarg; 465 | break; 466 | case '?': 467 | fprintf(stderr, "Unknown option: %c\n", optopt); 468 | return 1; 469 | } 470 | 471 | icon = create_tray_icon(start_icon); 472 | 473 | if (tooltip) { 474 | gtk_status_icon_set_tooltip_text(icon, tooltip); 475 | } 476 | 477 | /* optind holds the index of the next argument to be parsed */ 478 | /* getopt moved positional arguments (if there were any) to the end of the 479 | * argv array, without parsing them */ 480 | /* so if there were only non-positional arguments, all arguments have been 481 | * parsed and optind will be equal to argc */ 482 | if (optind < argc) { 483 | pipe = argv[optind]; 484 | reader = g_thread_new("watch_fifo", watch_fifo, pipe); 485 | } 486 | 487 | gtk_main(); 488 | return 0; 489 | } 490 | -------------------------------------------------------------------------------- /package/arch/.gitignore: -------------------------------------------------------------------------------- 1 | src 2 | pkg 3 | *.pkg.* 4 | -------------------------------------------------------------------------------- /package/arch/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Jon Gjengset 2 | _gitname=mktrayicon 3 | pkgname=$_gitname-git 4 | pkgver=11.df5ae8f 5 | pkgrel=1 6 | pkgdesc="Create system tray icons by writing to a pipe" 7 | arch=('any') 8 | url="https://github.com/Jonhoo/mktrayicon" 9 | license=('MIT') 10 | depends=('gtk3') 11 | makedepends=('git') 12 | provides=($_gitname) 13 | conflicts=($_gitname) 14 | install= 15 | source=('git+https://github.com/Jonhoo/mktrayicon.git') 16 | md5sums=('SKIP') 17 | 18 | pkgver() { 19 | cd $_gitname 20 | echo $(git rev-list --count HEAD).$(git rev-parse --short HEAD) 21 | } 22 | 23 | build() { 24 | cd $_gitname 25 | make 26 | } 27 | 28 | package() { 29 | cd $_gitname 30 | install -Dm 755 mktrayicon $pkgdir/usr/bin/mktrayicon 31 | } 32 | 33 | # vim:set ts=2 sw=2 et: 34 | --------------------------------------------------------------------------------