5 Originally written in 2011 by Tomasz M. Trzeciak, Public Domain
7 compiling with gcc (size optimized):
8 echo 1 ICON "tl-tray-menu.ico">tl-tray-menu.rc
9 windres tl-tray-menu.rc tl-tray-menu-rc.o
10 gcc -Os -s -mwindows -o tl-tray-menu.exe tl-tray-menu-rc.o tl-tray-menu.c
14 #define _WIN32_IE 0X0500 // minimum support for tray baloon (IE5)
23 static char strBuf
[MAX_STR
];
26 _snprintf( strBuf, 4*MAX_PATH, __VA_ARGS__ ); \
27 MessageBox( NULL, strBuf, "ERROR!", MB_ICONERROR | MB_SETFOREGROUND );\
31 #define IDI_ICON 1 // resource number of icon
33 #define WM_TRAYMSG 101
35 #define MAX_MENU_ENTRIES 31
36 #define MENU_CONFIG ( MAX_MENU_ENTRIES + 1 )
37 #define MENU_EXIT ( MAX_MENU_ENTRIES + 2 )
39 // buffer holding menu labels and commands
40 // (used only with config file)
41 static char menuStrings
[MAX_STR
];
43 // default menu labels
44 char *menuLabels
[MAX_MENU_ENTRIES
] = {
48 "PostScript/PDF &Viewer",
52 // default menu commands
53 char *menuCommands
[MAX_MENU_ENTRIES
] = {
54 "bin\\win32\\tlmgr-gui.exe",
55 "bin\\win32\\texdoctk.exe",
56 "bin\\win32\\texworks.exe",
57 "bin\\win32\\psv.exe",
58 "tlpkg\\installer\\tl-cmd.bat",
61 char configInfo
[4*MAX_PATH
] = "\
62 The menu can be customized with a configuration file.\n\
63 For an example and instructions see:\n";
68 LRESULT CALLBACK
WndProc( HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
74 hPopMenu
= CreatePopupMenu();
76 for ( i
= 0; menuLabels
[i
]; i
++ )
78 if ( menuCommands
[i
] )
79 AppendMenu( hPopMenu
, MF_STRING
, i
, menuLabels
[i
] );
81 AppendMenu( hPopMenu
, MF_SEPARATOR
, 0, NULL
);
82 AppendMenu( hPopMenu
, MF_STRING
, MENU_CONFIG
, "More..." );
83 AppendMenu( hPopMenu
, MF_STRING
, MENU_EXIT
, "E&xit" );
96 SetForegroundWindow( hWnd
); // needed to get keyboard focus
97 TrackPopupMenu( hPopMenu
, TPM_LEFTALIGN
, pnt
.x
, pnt
.y
, 0, hWnd
, NULL
);
106 switch ( LOWORD( wParam
) )
109 MessageBox( NULL
, configInfo
, nid
.szTip
, MB_SETFOREGROUND
);
113 Shell_NotifyIcon( NIM_DELETE
, &nid
);
120 PROCESS_INFORMATION pi
;
121 ZeroMemory( &si
, sizeof(si
) );
123 //si.dwFlags = STARTF_USESHOWWINDOW;
124 //si.wShowWindow = SW_HIDE ;
125 ZeroMemory( &pi
, sizeof(pi
) );
127 NULL
, // module name (uses command line if NULL)
128 menuCommands
[LOWORD(wParam
)], // command line
129 NULL
, // process security atrributes
130 NULL
, // thread security atrributes
131 TRUE
, // handle inheritance
132 0, // creation flags, e.g. CREATE_NO_WINDOW, DETACHED_PROCESS
133 NULL
, // pointer to environment block (uses parent if NULL)
134 NULL
, // starting directory (uses parent if NULL)
135 &si
, // STARTUPINFO structure
136 &pi
) // PROCESS_INFORMATION structure
137 ) DIE( "Failed to spawn command (error code %d):\n%s",
138 GetLastError(), menuCommands
[LOWORD(wParam
)] );
145 return DefWindowProc( hWnd
, msg
, wParam
, lParam
);
152 int APIENTRY
WinMain(
154 HINSTANCE hPrevInstance
,
163 // get file name of this executable
165 static char dirSelf
[MAX_PATH
];
166 DWORD nchars
= GetModuleFileName(NULL
, dirSelf
, MAX_PATH
);
167 if ( !nchars
|| ( nchars
== MAX_PATH
) ) DIE( "Cannot get own path." );
168 if ( s
= strrchr( dirSelf
, '\\' ) ) *s
= '\0'; // remove file name part
170 // set current directory
172 if ( !SetCurrentDirectory( dirSelf
) )
173 DIE( "Failed to change current directory to:\n%s", dirSelf
);
175 // prepend bin/win32 to PATH
177 strcpy( strBuf
, dirSelf
);
178 strcat( strBuf
, "\\bin\\win32;" );
179 strcat( strBuf
, getenv( "PATH" ) );
180 SetEnvironmentVariable( "PATH", strBuf
);
184 static char fileConfig
[MAX_PATH
];
185 s
= dirSelf
+ strlen(dirSelf
) + 1;
186 strcpy( fileConfig
, s
);
187 if ( s
= strrchr( fileConfig
, '.' ) ) *s
= '\0'; // remove file extension part
188 strcat( fileConfig
, ".ini" );
190 strcat( configInfo
, dirSelf
);
191 strcat( configInfo
, "\\tlpkg\\installer\\" );
192 strcat( configInfo
, fileConfig
);
194 if ( GetFileAttributes( fileConfig
) == INVALID_FILE_ATTRIBUTES
) {
196 // validate default configuration (menuCommands should be existing files)
199 for ( i
= 0; menuLabels
[i
]; i
++ )
200 if ( GetFileAttributes( menuCommands
[i
] ) == INVALID_FILE_ATTRIBUTES
)
201 menuCommands
[i
] = NULL
;
205 // read menu configuration from ini file
207 FILE *CONFIG
= fopen( fileConfig
, "rt" );
208 if ( CONFIG
== NULL
)
209 DIE( "Failed to open configuration file:\n%s\\%s", dirSelf
, fileConfig
);
211 char strSect
[] = "[menu]";
215 for ( lineNo
= 1 ; fgets( strBuf
, MAX_STR
, CONFIG
)
216 && menuItem
< MAX_MENU_ENTRIES
; lineNo
++ )
220 if ( *strBuf
== '[' &&
221 strncmp( strBuf
, strSect
, strlen( strSect
) ) == 0 ) menuItem
++;
224 if ( *strBuf
== ';' ) continue; // skip comments
225 if ( *strBuf
== '[' ) break; // next section
227 i
+= ExpandEnvironmentStrings( strBuf
, s
, MAX_STR
- i
);
229 DIE( "Exceeded 32KB limit for configuration." );
230 if ( i
-2 && menuStrings
[i
-2] == '\n' ) { menuStrings
[i
-2] = '\0'; i
--; }
231 menuLabels
[menuItem
] = s
;
232 while ( *s
&& *s
!= '=' ) s
++;
234 DIE( "Missing '=' on line %d in configuration file:\n%s\\%s",
235 lineNo
, dirSelf
, fileConfig
);
236 *s
++ = '\0'; // assign first, then increment
237 menuCommands
[menuItem
] = s
;
241 DIE( "Missing [menu] section in configuration file:\n%s\\%s",
242 dirSelf
, fileConfig
);
243 menuLabels
[menuItem
] = NULL
;
244 menuCommands
[menuItem
] = NULL
;
250 // create a hidden window for messaging
255 char szAppName
[] = "TrayMenu";
257 ZeroMemory( &wc
, sizeof wc
);
258 wc
.hInstance
= hInstance
;
259 wc
.lpszClassName
= szAppName
;
260 wc
.lpfnWndProc
= (WNDPROC
)WndProc
;
262 wc
.hbrBackground
= (HBRUSH
)GetStockObject(BLACK_BRUSH
);
263 wc
.hIcon
= LoadIcon( NULL
, IDI_APPLICATION
);
264 wc
.hCursor
= LoadCursor( NULL
, IDC_ARROW
);
266 if ( FALSE
== RegisterClass( &wc
) ) DIE( "Failed to register window class" );
281 if ( hWnd
== NULL
) DIE( "Failed to create window" );
285 nid
.cbSize
= sizeof nid
;
288 nid
.uFlags
= NIF_ICON
|NIF_MESSAGE
|NIF_TIP
;
289 nid
.hIcon
= LoadIcon( hInstance
, MAKEINTRESOURCE( IDI_ICON
) );
290 nid
.uCallbackMessage
= WM_TRAYMSG
;
291 strcpy( nid
.szTip
, "TeX Live Menu" );
292 #if (_WIN32_IE >= 0X0500)
293 nid
.uFlags
|= NIF_INFO
;
294 nid
.dwInfoFlags
= NIIF_INFO
;
295 strcpy( nid
.szInfo
, "Click on the tray icon\nto activate the menu." );
296 strcpy( nid
.szInfoTitle
, nid
.szTip
);
298 Shell_NotifyIcon( NIM_ADD
, &nid
);
300 // main message loop:
302 while ( GetMessage( &msg
, NULL
, 0, 0 ) > 0 )
304 TranslateMessage( &msg
);
305 DispatchMessage( &msg
);