beta-0.89.2
[luatex.git] / source / texk / texlive / w32_wrapper / tl-tray-menu.c
blobfe65d54515a02bbabdc3b9024349fd91a5f79869
1 /*
3 Simple tray menu
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)
16 #include <windows.h>
17 #include <process.h>
18 #include <stdlib.h>
19 #include <stdio.h>
21 #define MAX_STR 32768
23 static char strBuf[MAX_STR];
25 #define DIE(...) { \
26 _snprintf( strBuf, 4*MAX_PATH, __VA_ARGS__ ); \
27 MessageBox( NULL, strBuf, "ERROR!", MB_ICONERROR | MB_SETFOREGROUND );\
28 return 1; \
31 #define IDI_ICON 1 // resource number of icon
32 #define IDI_TRAY 100
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] = {
45 "&Package Manager",
46 "&Documentation",
47 "&Editor",
48 "PostScript/PDF &Viewer",
49 "&Command Prompt",
50 NULL };
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",
59 NULL };
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";
65 HMENU hPopMenu;
66 NOTIFYICONDATA nid;
68 LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
70 switch ( msg )
72 case WM_CREATE:
74 hPopMenu = CreatePopupMenu();
75 int i;
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" );
85 break;
87 case WM_TRAYMSG:
89 switch ( lParam )
91 case WM_LBUTTONUP:
92 case WM_RBUTTONUP:
94 POINT pnt;
95 GetCursorPos( &pnt );
96 SetForegroundWindow( hWnd ); // needed to get keyboard focus
97 TrackPopupMenu( hPopMenu, TPM_LEFTALIGN, pnt.x, pnt.y, 0, hWnd, NULL );
99 break;
102 break;
104 case WM_COMMAND:
106 switch ( LOWORD( wParam ) )
108 case MENU_CONFIG:
109 MessageBox( NULL, configInfo, nid.szTip, MB_SETFOREGROUND );
110 break;
112 case MENU_EXIT:
113 Shell_NotifyIcon( NIM_DELETE, &nid );
114 ExitProcess(0);
115 break;
117 default:
119 STARTUPINFO si;
120 PROCESS_INFORMATION pi;
121 ZeroMemory( &si, sizeof(si) );
122 si.cb = sizeof(si);
123 //si.dwFlags = STARTF_USESHOWWINDOW;
124 //si.wShowWindow = SW_HIDE ;
125 ZeroMemory( &pi, sizeof(pi) );
126 if( !CreateProcess(
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)] );
142 break;
144 default:
145 return DefWindowProc( hWnd, msg, wParam, lParam );
149 return 0;
152 int APIENTRY WinMain(
153 HINSTANCE hInstance,
154 HINSTANCE hPrevInstance,
155 LPSTR lpCmdLine,
156 int nCmdShow )
159 // scratch variables
161 char *s;
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 );
182 // configuration
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)
198 int i;
199 for ( i = 0; menuLabels[i]; i++ )
200 if ( GetFileAttributes( menuCommands[i] ) == INVALID_FILE_ATTRIBUTES )
201 menuCommands[i] = NULL;
203 } else {
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]";
212 int menuItem = -1;
213 int i = 0;
214 int lineNo;
215 for ( lineNo = 1 ; fgets( strBuf, MAX_STR, CONFIG )
216 && menuItem < MAX_MENU_ENTRIES ; lineNo++ )
218 if ( menuItem < 0 )
220 if ( *strBuf == '[' &&
221 strncmp( strBuf, strSect, strlen( strSect ) ) == 0 ) menuItem++;
222 continue;
224 if ( *strBuf == ';' ) continue; // skip comments
225 if ( *strBuf == '[' ) break; // next section
226 s = &menuStrings[i];
227 i += ExpandEnvironmentStrings( strBuf, s, MAX_STR - i );
228 if ( i >= MAX_STR )
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++;
233 if ( *s == '\0' )
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;
238 menuItem++;
240 if ( menuItem < 0 )
241 DIE( "Missing [menu] section in configuration file:\n%s\\%s",
242 dirSelf, fileConfig );
243 menuLabels[menuItem] = NULL;
244 menuCommands[menuItem] = NULL;
246 fclose( CONFIG );
250 // create a hidden window for messaging
252 MSG msg;
253 WNDCLASS wc;
254 HWND hWnd;
255 char szAppName[] = "TrayMenu";
257 ZeroMemory( &wc, sizeof wc );
258 wc.hInstance = hInstance;
259 wc.lpszClassName = szAppName;
260 wc.lpfnWndProc = (WNDPROC)WndProc;
261 wc.style = 0;
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" );
268 hWnd = CreateWindow(
269 szAppName,
272 CW_USEDEFAULT,
273 CW_USEDEFAULT,
274 CW_USEDEFAULT,
275 CW_USEDEFAULT,
278 hInstance,
281 if ( hWnd == NULL ) DIE( "Failed to create window" );
283 // tray icon stuff
285 nid.cbSize = sizeof nid;
286 nid.hWnd = hWnd;
287 nid.uID = IDI_TRAY;
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 );
297 #endif
298 Shell_NotifyIcon( NIM_ADD, &nid );
300 // main message loop:
302 while ( GetMessage( &msg, NULL, 0, 0 ) > 0 )
304 TranslateMessage( &msg );
305 DispatchMessage( &msg );
308 return msg.wParam;