Skip to content
Cross-platform, super tiny C99 implementation of a system tray icon with a popup menu.
Branch: master
Clone or download
zserge Merge pull request #6 from s-h-a-d-o-w/patch-1
[win] close menu if user clicks outside
Latest commit 8dd1358 Jul 31, 2018
Type Name Latest commit message Commit time
Failed to load latest commit information.
.gitattributes trying to override linguist language detection Jan 12, 2017
LICENSE Initial commit Jan 9, 2017
Makefile Updated Makefile Jul 23, 2018 Update Jul 23, 2018
example.c Remove platform-specific examples (Ref PR#2); add single platform che… Aug 22, 2017
icon.ico Add icon; Add platform-specific examples; Update readme with pre-requ… Aug 22, 2017
icon.png Remove platform-specific examples (Ref PR#2); add single platform che… Aug 22, 2017
tray.h [win] close menu if user clicks outside Jul 29, 2018


Cross-platform, single header, super tiny C99 implementation of a system tray icon with a popup menu.

Works well on:

  • Linux/Gtk (libappindicator)
  • Windows XP or newer (shellapi.h)
  • MacOS (Cocoa/AppKit)

There is also a stub implementation that returns errors on attempt to create a tray menu.


Before you can compile tray, you'll need to add an environment definition before the line where you include tray.h.

For Windows:

#include <stdio.h>
#include <string.h>

#define TRAY_WINAPI 1

#include "tray.h"

For Linux:

#include <stdio.h>
#include <string.h>


#include "tray.h"

For Mac:

#include <stdio.h>
#include <string.h>

#define TRAY_APPKIT 1

#include "tray.h"


The included example .c files can be compiled based on your environment.

For example, to compile and run the program on Windows:

$> gcc example_windows.c [Enter]

This will compile and build a.out. To run it:

$> a [Enter]


struct tray tray = {
    .icon = "icon.png",
    .menu = (struct tray_menu[]){{"Toggle me", 0, 0, toggle_cb, NULL},
                                 {"-", 0, 0, NULL, NULL},
                                 {"Quit", 0, 0, quit_cb, NULL},
                                 {NULL, 0, 0, NULL, NULL}},

void toggle_cb(struct tray_menu *item) {
	item->checked = !item->checked;

void quit_cb(struct tray_menu *item) {


while (tray_loop(1) == 0);


Tray structure defines an icon and a menu. Menu is a NULL-terminated array of items. Menu item defines menu text, menu checked and disabled (grayed) flags and a callback with some optional context pointer.

struct tray {
  char *icon;
  struct tray_menu *menu;

struct tray_menu {
  char *text;
  int disabled;
  int checked;

  void (*cb)(struct tray_menu *);
  void *context;

  struct tray_menu *submenu;
  • int tray_init(struct tray *) - creates tray icon. Returns -1 if tray icon/menu can't be created.
  • void tray_update(struct tray *) - updates tray icon and menu.
  • int tray_loop(int blocking) - runs one iteration of the UI loop. Returns -1 if tray_exit() has been called.
  • void tray_exit() - terminates UI loop.

All functions are meant to be called from the UI thread only.

Menu arrays must be terminated with a NULL item, e.g. the last item in the array must have text field set to NULL.


  • Cross-platform tray icon
  • Cross-platform tray popup menu
  • Separators in the menu
  • Disabled/enabled menu items
  • Checked/unchecked menu items
  • Nested menus
  • Icons for menu items
  • Rewrite ObjC code in C using ObjC Runtime (now ObjC code breaks many linters and static analyzers)
  • Call GTK code using dlopen/dlsym (to make binaries run safely if Gtk libraries are not available)


This software is distributed under MIT license, so feel free to integrate it in your commercial products.

You can’t perform that action at this time.