Fix buffer overflows in shortcut and workspace name handling
[wmaker-crm.git] / src / usermenu.c
blob55ee51bb8fae5a763251d130df89b3d4d57ad4c8
1 /* usermenu.c- user defined menu
3 * Window Maker window manager
5 * Copyright (c) hmmm... Should I put everybody's name here?
6 * Where's my lawyer?? -- ]d :D
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21 * USA.
23 * * * * * * * * *
24 * User defined menu is good, but beer's always better
25 * if someone wanna start hacking something, He heard...
26 * TODO
27 * - enhance commands. (eg, exit, hide, list all app's member
28 * window and etc)
29 * - cache menu... dunno.. if people really use this feature :P
30 * - Violins, senseless violins!
31 * that's all, right now :P
32 * - external! WINGs menu editor.
33 * TODONOT
34 * - allow applications to share their menu. ] think it
35 * looks wierd since there still are more than 1 appicon.
37 * Syntax...
38 * (
39 * "Program Name",
40 * ("Command 1", SHORTCUT, 1),
41 * ("Command 2", SHORTCUT, 2, ("Allowed_instant_1", "Allowed_instant_2")),
42 * ("Command 3", SHORTCUT, (3,4,5), ("Allowed_instant_1")),
43 * (
44 * "Submenu",
45 * ("Kill Command", KILL),
46 * ("Hide Command", HIDE),
47 * ("Hide Others Command", HIDE_OTHERS),
48 * ("Members", MEMBERS),
49 * ("Exit Command", EXIT)
50 * )
51 * )
53 * Tips:
54 * - If you don't want short cut keys to be listed
55 * in the right side of entries, you can just put them
56 * in array instead of using the string directly.
60 #include "wconfig.h"
62 #ifdef USER_MENU
64 #include <X11/Xlib.h>
65 #include <X11/Xutil.h>
66 #include <X11/Xproto.h>
67 #include <X11/Xatom.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <stdio.h>
71 #include <unistd.h>
73 #include "WindowMaker.h"
74 #include "wcore.h"
75 #include "menu.h"
76 #include "actions.h"
77 #include "funcs.h"
78 #include "keybind.h"
80 #include "framewin.h"
82 #define MAX_SHORTCUT_LENGTH 32
84 /*** var ***/
85 extern WPreferences wPreferences;
87 typedef struct {
88 WScreen *screen;
89 WShortKey *key;
90 int key_no;
91 } WUserMenuData;
94 static void
95 notifyClient(WMenu *menu, WMenuEntry *entry)
97 XEvent event;
98 WUserMenuData *data = entry->clientdata;
99 WScreen *scr = data->screen;
100 Window window;
101 int i;
103 window=scr->focused_window->client_win;
105 for(i=0;i<data->key_no;i++) {
106 event.xkey.type = KeyPress;
107 event.xkey.display = dpy;
108 event.xkey.window = window;
109 event.xkey.root = DefaultRootWindow(dpy);
110 event.xkey.subwindow = (Window)None;
111 event.xkey.x = 0x0;
112 event.xkey.y = 0x0;
113 event.xkey.x_root = 0x0;
114 event.xkey.y_root = 0x0;
115 event.xkey.keycode = data->key[i].keycode;
116 event.xkey.state = data->key[i].modifier;
117 event.xkey.same_screen = True;
118 event.xkey.time = CurrentTime;
119 if (XSendEvent(dpy, window, False, KeyPressMask, &event)) {
120 event.xkey.type = KeyRelease;
121 event.xkey.time = CurrentTime;
122 XSendEvent(dpy, window, True, KeyReleaseMask, &event);
127 static void
128 removeUserMenudata(void *menudata)
130 WUserMenuData *data = menudata;
131 if(data->key) wfree(data->key);
132 wfree(data);
136 static WUserMenuData*
137 convertShortcuts(WScreen *scr, WMPropList *shortcut)
139 WUserMenuData *data;
140 KeySym ksym;
141 char *k;
142 char *buffer;
143 char buf[MAX_SHORTCUT_LENGTH], *b;
144 int keycount,i,j,mod;
146 if (WMIsPLString(shortcut)) {
147 keycount = 1;
149 else if (WMIsPLArray(shortcut)) {
150 keycount = WMGetPropListItemCount(shortcut);
152 else return NULL;
153 /*for (i=0;i<keycount;i++){*/
155 data = wmalloc(sizeof(WUserMenuData));
156 if (!data) return NULL;
157 data->key = wmalloc(sizeof(WShortKey)*keycount);
158 if (!data->key) {
159 wfree(data);
160 return NULL;
164 for (i=0,j=0;i<keycount;i++) {
165 data->key[j].modifier = 0;
166 if (WMIsPLArray(shortcut)) {
167 strncpy(buf, WMGetFromPLString(WMGetFromPLArray(shortcut, i)),
168 MAX_SHORTCUT_LENGTH);
169 } else {
170 strncpy(buf, WMGetFromPLString(shortcut), MAX_SHORTCUT_LENGTH);
172 b = (char*)buf;
174 while ((k = strchr(b, '+'))!=NULL) {
175 *k = 0;
176 mod = wXModifierFromKey(b);
177 if (mod<0) {
178 break;
180 data->key[j].modifier |= mod;
181 b = k+1;
184 ksym = XStringToKeysym(b);
185 if (ksym==NoSymbol) {
186 continue;
189 data->key[j].keycode = XKeysymToKeycode(dpy, ksym);
190 if (data->key[j].keycode) {
191 j++;
195 keyover:
197 /* get key */
198 if (!j) {
199 puts("fatal j");
200 wfree(data->key);
201 wfree(data);
202 return NULL;
204 data->key_no = j;
205 data->screen = scr;
207 return data;
210 static WMenu*
211 configureUserMenu(WScreen *scr, WMPropList *plum)
213 char *mtitle;
214 WMenu *menu=NULL;
215 WMPropList *elem, *title, *command, *params;
216 int count,i;
217 WUserMenuData *data;
219 if (!plum) return NULL;
220 if (!WMIsPLArray(plum)) {
221 return NULL;
224 count = WMGetPropListItemCount(plum);
225 if (!count) return NULL;
227 elem = WMGetFromPLArray(plum, 0);
228 if (!WMIsPLString(elem)) {
229 return NULL;
232 mtitle = WMGetFromPLString(elem);
234 menu=wMenuCreateForApp(scr, mtitle, True);
236 for(i=1; i<count; i++) {
237 elem = WMGetFromPLArray(plum,i);
238 if(WMIsPLArray(WMGetFromPLArray(elem,1))) {
239 WMenu *submenu;
240 WMenuEntry *mentry;
242 submenu = configureUserMenu(scr,elem);
243 if (submenu)
244 mentry = wMenuAddCallback(menu, submenu->frame->title,
245 NULL, NULL);
246 wMenuEntrySetCascade(menu, mentry, submenu);
248 else {
249 int idx = 0;
250 WMPropList *instances=0;
252 title = WMGetFromPLArray(elem,idx++);
253 command = WMGetFromPLArray(elem,idx++);
254 if (WMGetPropListItemCount(elem) >= 3)
255 params = WMGetFromPLArray(elem,idx++);
257 if (!title || !command)
258 return menu;
260 if (!strcmp("SHORTCUT",WMGetFromPLString(command))) {
261 WMenuEntry *entry;
263 data = convertShortcuts(scr, params);
264 if (data){
265 entry = wMenuAddCallback(menu, WMGetFromPLString(title),
266 notifyClient, data);
268 if (entry) {
269 if (WMIsPLString(params)) {
270 entry->rtext = GetShortcutString(WMGetFromPLString(params));
272 entry->free_cdata = removeUserMenudata;
274 if (WMGetPropListItemCount(elem) >= 4) {
275 instances = WMGetFromPLArray(elem,idx++);
276 if(WMIsPLArray(instances))
277 if (instances && WMGetPropListItemCount(instances)
278 && WMIsPLArray(instances)){
279 entry->instances = WMRetainPropList(instances);
289 return menu;
292 void
293 wUserMenuRefreshInstances(WMenu *menu, WWindow *wwin)
295 WMenuEntry* entry;
296 int i,j,count,paintflag;
298 paintflag=0;
300 if(!menu) return;
302 for (i=0; i<menu->entry_no; i++) {
303 if (menu->entries[i]->instances){
304 WMPropList *ins;
305 int oldflag;
306 count = WMGetPropListItemCount(menu->entries[i]->instances);
308 oldflag = menu->entries[i]->flags.enabled;
309 menu->entries[i]->flags.enabled = 0;
310 for (j=0; j<count;j++) {
311 ins = WMGetFromPLArray(menu->entries[i]->instances,j);
312 if (!strcmp(wwin->wm_instance,WMGetFromPLString(ins))) {
313 menu->entries[i]->flags.enabled = 1;
314 break;
317 if (oldflag != menu->entries[i]->flags.enabled)
318 paintflag=1;
321 for (i=0; i < menu->cascade_no; i++) {
322 if (!menu->cascades[i]->flags.brother)
323 wUserMenuRefreshInstances(menu->cascades[i], wwin);
324 else
325 wUserMenuRefreshInstances(menu->cascades[i]->brother, wwin);
328 if (paintflag)
329 wMenuPaint(menu);
333 static WMenu*
334 readUserMenuFile(WScreen *scr, char *file_name)
336 WMenu *menu;
337 char *mtitle;
338 WMPropList *plum, *elem, *title, *command, *params;
339 int count,i;
341 menu=NULL;
342 plum = WMReadPropListFromFile(file_name);
343 /**/
345 if(plum){
346 menu = configureUserMenu(scr, plum);
347 WMReleasePropList(plum);
349 return menu;
353 WMenu*
354 wUserMenuGet(WScreen *scr, WWindow *wwin)
356 WMenu *menu = NULL;
357 char buffer[100];
358 char *path = NULL;
359 char *tmp;
360 if (wwin->wm_instance && wwin->wm_class) {
361 int len = strlen(wwin->wm_instance)+strlen(wwin->wm_class)+7;
362 tmp=wmalloc(len);
363 snprintf(tmp,len,"%s.%s.menu",wwin->wm_instance,wwin->wm_class);
364 path = wfindfile(DEF_USER_MENU_PATHS,tmp);
365 wfree(tmp);
367 if (!path) return NULL;
369 if (wwin) {
370 menu = readUserMenuFile(scr, path);
373 wfree(path);
375 return menu;
378 #endif /* USER_MENU */