1 /*
2 xrus - keyboard switcher/indicator and autolock.
3 Copyright (c) 1995-2001 Alexander V. Lukyanov
4 This is free software with no warranty.
5 See COPYING for details.
6 */
7 /*__________________________________________________________________________
8 **
9 ** File: xrus.c
10 **__________________________________________________________________________
11 */
12
13 #include <config.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <errno.h>
17 #include <time.h>
18 #include <ctype.h>
19 #include <sys/types.h>
20 #include <signal.h>
21 #include <fcntl.h>
22 #ifdef HAVE_UNISTD_H
23 # include <unistd.h>
24 #endif
25 #ifdef HAVE_SYS_WAIT_H
26 # include <sys/wait.h>
27 #endif
28
29 #include <X11/Xlib.h>
30 #include <X11/Xatom.h>
31 #include <X11/Intrinsic.h>
32 #include <X11/StringDefs.h>
33 #include <X11/Shell.h>
34 #include <X11/Xmd.h>
35 #include <X11/Xlibint.h> /* for resource_mask */
36
37 #if TK==TK_MOTIF
38 #include <Xm/Xm.h>
39 #include <Xm/PushB.h>
40 #include <Xm/Form.h>
41 #include <Xm/MwmUtil.h>
42 #elif TK==TK_XAW
43 #include <X11/Xaw/Command.h>
44 #include <X11/Xaw/Paned.h>
45 #include <X11/Xaw/Box.h>
46 #endif
47
48 #define XK_MISCELLANY
49 #include <X11/keysymdef.h>
50
51 #ifndef SA_RESTART
52 #define SA_RESTART 0
53 #endif
54
55 #ifndef R_OK
56 # define R_OK 4
57 #endif
58
59 #include "xalloca.h"
60
61 #include "keycomb.h"
62 #include "xrus.h"
63 #include "props.h"
64 #include "menu.h"
65 #include "kbdstate.h"
66
67 #include "confpaths.h"
68
69 const char AppClass[]="Xrus";
70
71 extern const char *DefaultResources;
72
73 XtActionsRec Actions[]=
74 {
75 {"XrusMenuPopup",XrusMenuPopup}
76 };
77
78 struct wincache_cell
79 {
80 Window win;
81 time_t time;
82 }
83 window_cache[256];
84
85 XrusRec AppData;
86
87 char DefaultSwitchKeys[]="Shift_L+Shift_R";
88 char DefaultToLatKeys[]="";
89 char DefaultToRusKeys[]="";
90 char DefaultSwitchForOneKeys[]="";
91 char DefaultLocker[]="exec xlock -remote >/dev/null 2>&1";
92
93 char LockerData[256];
94 char SwitchKeysData[256];
95 char ToLatKeysData[256];
96 char ToRusKeysData[256];
97 char SwitchForOneKeysData[256];
98
99 XtResource resources[]=
100 {
101 /* { name class type size
102 offset
103 default_type default_addr },*/
104 /* { ----------- ----------- ----------- ------------
105 -----------------------------------
106 ----------- ------------------------- },*/
107 { "autolock", "Autolock", XtRBoolean, sizeof(Boolean),
108 XtOffsetOf(XrusRec,autolock),
109 XtRImmediate, (XtPointer)False },
110 { "locker", "Locker", XtRString, sizeof(String),
111 XtOffsetOf(XrusRec,locker),
112 XtRString, DefaultLocker },
113 { "timeout", "Timeout", XtRInt, sizeof(int),
114 XtOffsetOf(XrusRec,timeout),
115 XtRImmediate, (XtPointer)30 },
116 { "useBell", "UseBell", XtRBoolean, sizeof(int),
117 XtOffsetOf(XrusRec,useBell),
118 XtRImmediate, (XtPointer)False },
119 { "switchKeys","SwitchKeys",XtRString,sizeof(String),
120 XtOffsetOf(XrusRec,switchKeys),
121 XtRString, DefaultSwitchKeys },
122 { "toRusKeys","ToRusKeys",XtRString, sizeof(String),
123 XtOffsetOf(XrusRec,toRusKeys),
124 XtRString, DefaultToRusKeys },
125 { "toLatKeys","ToLatKeys",XtRString, sizeof(String),
126 XtOffsetOf(XrusRec,toLatKeys),
127 XtRString, DefaultToLatKeys },
128 { "switchForOneKeys","SwitchForOneKeys",XtRString,sizeof(String),
129 XtOffsetOf(XrusRec,switchForOneKeys),
130 XtRString, DefaultSwitchForOneKeys },
131 { "xmodmap", "Xmodmap", XtRString, sizeof(String),
132 XtOffsetOf(XrusRec,xmodmap),
133 XtRString, "" },
134 { "led", "Led", XtRInt, sizeof(int),
135 XtOffsetOf(XrusRec,led),
136 XtRImmediate, (XtPointer)0 },
137 { "altMaps", "AltMaps", XtRString, sizeof(String),
138 XtOffsetOf(XrusRec,altMaps),
139 XtRString, "" },
140 { "icon", "Icon", XtRBoolean, sizeof(Boolean),
141 XtOffsetOf(XrusRec,icon),
142 XtRImmediate, (XtPointer)True },
143 { "adjustModeButtons","AdjustModeButtons",XtRBoolean,sizeof(Boolean),
144 XtOffsetOf(XrusRec,adjustModeButtons),
145 XtRImmediate, (XtPointer)True },
146 { "alwaysOnTop","AlwaysOnTop",XtRBoolean,sizeof(Boolean),
147 XtOffsetOf(XrusRec,alwaysOnTop),
148 XtRImmediate, (XtPointer)False },
149 { "alwaysMapped","AutoMap",XtRBoolean,sizeof(Boolean),
150 XtOffsetOf(XrusRec,alwaysMapped),
151 XtRImmediate, (XtPointer)False },
152 { "capsLockEmu","CapsLockEmu",XtRBoolean,sizeof(Boolean),
153 XtOffsetOf(XrusRec,capsLockEmu),
154 XtRImmediate, (XtPointer)False },
155 { "capsLockLed","CapsLockLed",XtRInt, sizeof(int),
156 XtOffsetOf(XrusRec,capsLockLed),
157 XtRImmediate, (XtPointer)0 },
158 { "recheckTime","RecheckTime",XtRInt, sizeof(int),
159 XtOffsetOf(XrusRec,recheckTime),
160 XtRImmediate, (XtPointer)1500 },
161 { "noFork", "NoFork", XtRBoolean, sizeof(Boolean),
162 XtOffsetOf(XrusRec,noFork),
163 XtRImmediate, (XtPointer)False },
164 { "wmIcon", "WMIcon", XtRBoolean, sizeof(Boolean),
165 XtOffsetOf(XrusRec,wm_icon),
166 XtRImmediate, (XtPointer)False },
167 { "wMaker", "WMaker", XtRBoolean, sizeof(Boolean),
168 XtOffsetOf(XrusRec,wmaker_icon),
169 XtRImmediate, (XtPointer)False },
170 { "xmodmapProgram", "XmodmapProgram", XtRString, sizeof(String),
171 XtOffsetOf(XrusRec,xmodmap_program),
172 XtRString, BINDIR "/xrusmodmap" },
173 { "title0", "Title0", XtRString, sizeof(String),
174 XtOffsetOf(XrusRec,title0),
175 XtRString, (XtPointer)0 },
176 { "title1", "Title1", XtRString, sizeof(String),
177 XtOffsetOf(XrusRec,title1),
178 XtRString, (XtPointer)0 },
179 { "perWindow","PerWindow",XtRBoolean, sizeof(Boolean),
180 XtOffsetOf(XrusRec,per_window_state),
181 XtRImmediate, (XtPointer)False },
182 { "titlePerWindow0","TitlePerWindow0",XtRString,sizeof(String),
183 XtOffsetOf(XrusRec,titlePerWindow0),
184 XtRString, (XtPointer)0 },
185 { "titlePerWindow1","TitlePerWindow1",XtRString,sizeof(String),
186 XtOffsetOf(XrusRec,titlePerWindow1),
187 XtRString, (XtPointer)0 },
188 { "occupyAllDesks","OccupyAllDesks",XtRBoolean, sizeof(Boolean),
189 XtOffsetOf(XrusRec,occupyAllDesks),
190 XtRImmediate, (XtPointer)True },
191 { "keyLog", "KeyLog", XtRString, sizeof(String),
192 XtOffsetOf(XrusRec,keylog_file),
193 XtRString, (XtPointer)0 },
194 };
195
196 XrmOptionDescRec options[]=
197 {
198 /* { option, specifier, argKind, value },*/
199 /* { ------------- -------------- ----------------- ------ },*/
200 { "-autolock", "*autolock", XrmoptionNoArg, "true" },
201 { "+autolock", "*autolock", XrmoptionNoArg, "false"},
202 { "-locker", "*locker", XrmoptionSepArg, NULL },
203 { "-timeout", "*timeout", XrmoptionSepArg, NULL },
204 { "-bell", "*useBell", XrmoptionNoArg, "true" },
205 { "+bell", "*useBell", XrmoptionNoArg, "false"},
206 { "-led", "*led", XrmoptionSepArg, "0" },
207 { "+led", "*led", XrmoptionNoArg, "0" },
208 { "-icon", "*icon", XrmoptionNoArg, "true" },
209 { "+icon", "*icon", XrmoptionNoArg, "false"},
210 { "-nofork", "*noFork", XrmoptionNoArg, "true" },
211 { "+nofork", "*noFork", XrmoptionNoArg, "false"},
212 { "-fork", "*noFork", XrmoptionNoArg, "false"},
213 { "+fork", "*noFork", XrmoptionNoArg, "true" },
214 { "-wmicon", "*wmIcon", XrmoptionNoArg, "true" },
215 { "-wmaker", "*wMaker", XrmoptionNoArg, "true" },
216 { "-perwindow", "*perWindow", XrmoptionNoArg, "true" },
217 { "-keylog", "*keyLog", XrmoptionSepArg, NULL },
218 };
219
220 Display *disp;
221 Atom wm_delete_window;
222 Atom wm_protocols;
223 XtAppContext app_context;
224
225 #define StartArgs() count=0
226 #define AddArg(name,val) (XtSetArg(args[count],(name),(val)),++count)
227
228 Arg args[32];
229 int count;
230
231 char *program;
232
233 struct KeyCombination SwitchKeys,ToLatKeys,ToRusKeys,SwitchForOneKeys;
234
235 #define ShouldUseLat() IsModifierPressed(ControlMapIndex)
236
237 enum
238 {
239 MODE=1,
240 RUS=1,
241 LAT=0,
242 TEMP_LAT=2,
243 CAPSLOCK_ON=4,
244 FOR_ONE=8,
245 SAVE_MASK=MODE
246 };
247 int Mode=LAT;
248 int NewMode;
249
250 Widget top_level=NULL;
251 Window our_host_window=0;
252 #if TK!=TK_NONE
253 Widget form_w;
254 Widget switch_button[2];
255 #endif
256
257 Atom type_ret;
258 int format_ret;
259 unsigned long nitems_ret,bytes_after_ret;
260 unsigned char *prop;
261
262 static Atom kbd_state_atom;
263 static Window focus_window;
264 static int revert_to=RevertToNone;
265 static Window last_top_level_focus_window;
266 static int last_top_level_revert_to=RevertToNone;
267 static Bool pushed=False;
268
269 static int xrus_check=1;
270
271 static int keylog_fd=-1;
272 static int keylog_pos=0;
273 static time_t keylog_time=0;
274
275 void SetAlarm();
276
277 int (*old_error_handler)(Display *d,XErrorEvent *ev);
278
279 pid_t LockerRunning=0;
280
281 time_t alarm_expected;
282
283 static int MappingNotifyIgnoreCount=0;
284 static XtIntervalId MappingNotifyTimeout=-1;
285
286 #if TK!=TK_NONE
287 static void ShowSwitchButton(void);
288 static void SetTitle(int);
289 #else
290 # define ShowSwitchButton()
291 # define SetTitle(x)
292 #endif
293
294 #define MaxLetterKeySym 0xF000
295 #define NationalKeySym(ks) (ks>=128 && ks<MaxLetterKeySym)
296 #define AsciiKeySym(ks) (ks>0 && ks<128)
297
298 XID client_id_mask;
299 #define ClientId(w) ((w)&client_id_mask)
300
301 /*________________________________________________________________________
302 */
303
304
305 /* SwappableKey, modified by Tobias Ernst:
306
307 Now returns 0 if this key is not to be swapped, non-zero otherwise. The
308 non-zero number indicates how many keysyms are grouped into one block. These
309 are two in a standard English-Russian setup, but could be four e.g. in a
310 German-Russian setup (keysyms 0 and 1 for Latin letters plus keysyms 2 and 3
311 for "AltGr" key combinations in a group, and keysyms 4 and 5 for cyrillic
312 letters and 6 and 7 with NoSymbol in the other group.) */
313
SwappableKey(KeySym * k)314 static int SwappableKey(KeySym *k)
315 {
316 int i, j = 0;
317
318 for (i = 2; i <= (keysyms_per_keycode / 2); i += 2)
319 {
320 if (k[i] > 0 && k[i]<MaxLetterKeySym)
321 {
322 j = i;
323 }
324 }
325
326 for (i = 0; i < 2*j; i++)
327 {
328 if (!(k[i] >= 0 && k[i] < MaxLetterKeySym))
329 {
330 /* nonprintable character found other than NoSymbol found,
331 and we don't want to swap this */
332 return 0;
333 }
334 }
335
336 return j;
337 }
338
CapsLockableKey(KeySym * k)339 static int CapsLockableKey(KeySym *k)
340 {
341 KeySym k0=k[0],k1=k[1];
342 return((NationalKeySym(k0) && NationalKeySym(k1))
343 || (((k0>='a' && k0<='z') || (k0>='A' && k0<='Z'))
344 &&
345 ((k1>='a' && k1<='z') || (k1>='A' && k1<='Z'))));
346 }
347
FixNewMode()348 void FixNewMode()
349 {
350 NewMode&=~(TEMP_LAT);
351 if(ShouldUseLat())
352 NewMode|=TEMP_LAT;
353 }
354
SetTitleIndicator(Window w,int mode)355 static void SetTitleIndicator(Window w,int mode)
356 {
357 XTextProperty prop;
358 char *new_title, *append, *old;
359 int old_len, append_len, title_len;
360
361 if((!AppData.titlePerWindow0 || !AppData.titlePerWindow1)
362 || (!AppData.titlePerWindow0[0] && !AppData.titlePerWindow1[0]))
363 return;
364
365 if(!XGetWMName(disp,w,&prop) || prop.format!=8)
366 return;
367
368 mode&=MODE;
369
370 append=mode?AppData.titlePerWindow1:AppData.titlePerWindow0;
371 old =mode?AppData.titlePerWindow0:AppData.titlePerWindow1;
372 new_title=alloca(prop.nitems+256);
373 old_len=strlen(old);
374 append_len=strlen(append);
375
376 memcpy(new_title,prop.value,title_len=prop.nitems);
377 new_title[title_len]=0; /* for strcmp below */
378 XFree(prop.value);
379
380 if(title_len>old_len
381 && !strcmp(new_title+title_len-old_len,old))
382 title_len-=old_len;
383 else if(title_len>append_len
384 && !strcmp(new_title+title_len-append_len,append))
385 return;
386
387 if(title_len==0)
388 {
389 strcpy(new_title,"untitled");
390 title_len=strlen(new_title);
391 }
392 if(new_title[title_len-1]!=' ')
393 new_title[title_len++]=' ';
394 strcpy(new_title+title_len,append);
395 title_len+=append_len;
396
397 prop.nitems=title_len;
398 prop.value=new_title;
399 XSetWMName(disp,w,&prop);
400 }
401
GetKbdState(Window w)402 static int GetKbdState(Window w)
403 {
404 long *prop=0;
405 if(XGetWindowProperty(disp,w,kbd_state_atom,0L,256L,0,XA_CARDINAL,
406 &type_ret,&format_ret,&nitems_ret,&bytes_after_ret,(void*)&prop)
407 ==Success && type_ret==XA_CARDINAL && nitems_ret==1 && prop)
408 {
409 int ret=(prop[0]&SAVE_MASK);
410 XFree(prop);
411 return ret;
412 }
413 return -1;
414 }
415
416 static XtIntervalId SetTitleTimeout=-1;
SetTitleIndicatorOnTimeout(XtPointer closure,XtIntervalId * id)417 static void SetTitleIndicatorOnTimeout(XtPointer closure,XtIntervalId *id)
418 {
419 Window w=(Window)closure;
420 int mode=GetKbdState(w);
421 if(mode!=-1)
422 SetTitleIndicator(w,mode);
423 SetTitleTimeout=-1;
424 }
425
window_is_top_level(Window w)426 static Bool window_is_top_level(Window w)
427 {
428 int num,i;
429 Atom *list=XListProperties(disp,w,&num);
430
431 if(!list)
432 return False;
433
434 for(i=0; i<num; i++)
435 {
436 if(list[i]==XA_WM_NAME
437 || list[i]==XA_WM_CLASS
438 || list[i]==XA_WM_COMMAND)
439 {
440 XFree(list);
441 return True;
442 }
443 }
444 XFree(list);
445 return False;
446 }
447
window_is_ours(Window w)448 static Bool window_is_ours(Window w)
449 {
450 if(!top_level)
451 return False;
452 return (ClientId(w)==ClientId(XtWindow(top_level)));
453 /* Also possibe:
454 return (XtWindowToWidget(disp,w)!=NULL); */
455 }
456
SaveModeForWindow(Window w)457 static void SaveModeForWindow(Window w)
458 {
459 long m=Mode&SAVE_MASK;
460 if(!AppData.per_window_state)
461 return;
462 if(w==0 || !window_is_top_level(w))
463 w=last_top_level_focus_window;
464 if(w==0)
465 return;
466 SetTitleIndicator(w,Mode);
467 XChangeProperty(disp,w,kbd_state_atom,XA_CARDINAL,32,
468 PropModeReplace,(void*)&m,1);
469 }
470
SwitchKeyboard(int to)471 void SwitchKeyboard(int to)
472 {
473 KeySym tmp;
474 KeySym *keymap;
475 int i, j, groupwidth;
476 XKeyboardControl kbd_control;
477 int from_mode,to_mode,from_caps,to_caps;
478 int changed=0;
479 int from=Mode;
480
481 if(to==Mode)
482 return;
483
484 keymap=GetKeymap();
485 if(keymap==0)
486 return;
487
488 if(keysyms_per_keycode<4) /* should not happen */
489 return;
490
491 from_mode=((Mode&TEMP_LAT)?LAT:(Mode&FOR_ONE)?((Mode&MODE)^MODE):Mode&MODE);
492 to_mode= ((to &TEMP_LAT)?LAT:(to &FOR_ONE)?((to &MODE)^MODE):to &MODE);
493
494 from_caps=((Mode&TEMP_LAT)?0:Mode&CAPSLOCK_ON);
495 to_caps=((to&TEMP_LAT)?0:to&CAPSLOCK_ON);
496
497 for(i=(max_keycode-min_keycode)*keysyms_per_keycode;
498 i>=0; i-=keysyms_per_keycode)
499 {
500 groupwidth = SwappableKey(keymap + i);
501
502 if(from_mode!=to_mode && groupwidth!=0)
503 {
504 for (j=0; j < groupwidth; j++)
505 {
506 tmp=keymap[i+j];
507 keymap[i+j]=keymap[i+j+groupwidth];
508 keymap[i+j+groupwidth]=tmp;
509 }
510 changed=1;
511 }
512
513 if(AppData.capsLockEmu && from_caps!=to_caps)
514 {
515 if(CapsLockableKey(keymap+i))
516 {
517 tmp=keymap[i];
518 keymap[i]=keymap[i+1];
519 keymap[i+1]=tmp;
520 changed=1;
521 }
522 if(groupwidth && CapsLockableKey(keymap+i+groupwidth))
523 {
524 tmp=keymap[i+groupwidth];
525 keymap[i+groupwidth]=keymap[i+groupwidth+1];
526 keymap[i+groupwidth+1]=tmp;
527 changed=1;
528 }
529 }
530 }
531 if(changed)
532 {
533 XChangeKeyboardMapping(disp,min_keycode,keysyms_per_keycode,
534 keymap,max_keycode-min_keycode+1);
535 MappingNotifyIgnoreCount++;
536
537 /* SGI X server goes crazy without this */
538 XSetModifierMapping(disp,GetModifiers());
539 }
540
541 if(AppData.capsLockEmu && from_caps!=to_caps)
542 {
543 if(AppData.capsLockLed>0 && AppData.capsLockLed<=32)
544 {
545 kbd_control.led_mode=(NewMode&CAPSLOCK_ON?LedModeOn:LedModeOff);
546 kbd_control.led=AppData.capsLockLed;
547 XChangeKeyboardControl(disp,KBLed|KBLedMode,&kbd_control);
548 }
549 }
550
551 if((Mode&MODE)!=(to&MODE))
552 {
553 if(AppData.led>0 && AppData.led<=32)
554 {
555 kbd_control.led_mode=((to&MODE)==RUS?LedModeOn:LedModeOff);
556 kbd_control.led=AppData.led;
557 XChangeKeyboardControl(disp,KBLed|KBLedMode,&kbd_control);
558 }
559
560 if(AppData.useBell)
561 XBell(disp,30);
562 }
563 Mode=to;
564 if((from&SAVE_MASK) != (to&SAVE_MASK))
565 SaveModeForWindow(focus_window);
566 ShowSwitchButton();
567 }
568
SwitchKeyboardForWindow(Window w)569 static void SwitchKeyboardForWindow(Window w)
570 {
571 int mode;
572 Window old_last_top_level_focus_window=last_top_level_focus_window;
573 Window old_focus_window=focus_window;
574 if(w==0)
575 return;
576 focus_window=0;
577 last_top_level_focus_window=0;
578 mode=GetKbdState(w);
579 if(mode==-1)
580 mode=0;
581 NewMode=(Mode&~SAVE_MASK)|mode;
582
583 NewMode&=~FOR_ONE;
584 FixNewMode();
585 SwitchKeyboard(NewMode);
586
587 if(prop)
588 XFree(prop);
589
590 focus_window=old_focus_window;
591 last_top_level_focus_window=old_last_top_level_focus_window;
592 }
InitCapsLockEmu()593 static void InitCapsLockEmu()
594 {
595 KeyCode *lock;
596 int i,changed=0;
597 XModifierKeymap *mod;
598
599 if(!AppData.capsLockEmu)
600 return;
601
602 mod=XGetModifierMapping(disp);
603 lock=mod->modifiermap
604 +LockMapIndex*mod->max_keypermod;
605 for(i=0; i<mod->max_keypermod; i++)
606 {
607 if(lock[i])
608 {
609 changed=1;
610 lock[i]=0;
611 }
612 }
613 if(changed && XSetModifierMapping(disp,mod)!=MappingSuccess)
614 {
615 fprintf(stderr,"%s: can't remove Caps_Lock from lock -- CapsLock emulation disabled\n",program);
616 AppData.capsLockEmu=False;
617 }
618 XFreeModifiermap(mod);
619 }
620
621 static const char *load_map=0;
load_map_delayed(const char * map)622 void load_map_delayed(const char *map)
623 {
624 if(MappingNotifyIgnoreCount==0)
625 run_xmodmap(map);
626 else
627 load_map=map;
628 }
629
630 #if TK!=TK_NONE
RaiseButton()631 static void RaiseButton()
632 {
633 if(AppData.wm_icon || AppData.wmaker_icon || top_level==0)
634 return;
635 XRaiseWindow(disp,XtWindow(top_level));
636 }
637
638 static XtIntervalId raise_tmout=-1;
RaiseButtonOnTimeout(XtPointer closure,XtIntervalId * id)639 static void RaiseButtonOnTimeout(XtPointer closure,XtIntervalId *id)
640 {
641 (void)closure; (void)id;
642 raise_tmout=-1;
643 RaiseButton();
644 }
645
SetTitle(int n)646 static void SetTitle(int n)
647 {
648 const char *title=(n==0 ? AppData.title0 : AppData.title1);
649 if(title==0)
650 title="xrus";
651 XtSetArg(args[0],XtNtitle,title);
652 XtSetArg(args[1],XtNiconName,title);
653 XtSetValues(top_level,args,2);
654 }
655
656 /* shows proper indicator button */
ShowSwitchButton(void)657 static void ShowSwitchButton(void)
658 {
659 int to_manage=-1;
660
661 if(!switch_button[0] || !switch_button[1])
662 return; /* no button to show ! */
663
664 if((Mode&MODE)==RUS && !XtIsManaged(switch_button[1]))
665 to_manage=1;
666 else if((Mode&MODE)==LAT && !XtIsManaged(switch_button[0]))
667 to_manage=0;
668
669 if(to_manage!=-1)
670 {
671 XtSetArg(args[0],XtNallowShellResize,False);
672 XtSetValues(top_level,args,1);
673
674 XtUnmanageChild(switch_button[1-to_manage]);
675
676 XtSetArg(args[0],XtNallowShellResize,True);
677 XtSetValues(top_level,args,1);
678
679 XtManageChild(switch_button[to_manage]);
680
681 SetTitle(to_manage);
682
683 RaiseButton();
684 }
685 }
686
UnmapHandler(Widget w,XtPointer closure,XEvent * ev,Boolean * cont)687 void UnmapHandler(Widget w,XtPointer closure,XEvent *ev,Boolean *cont)
688 {
689 *cont=True;
690 if(ev->type==UnmapNotify && AppData.alwaysMapped && AppData.icon
691 && !AppData.wm_icon && !AppData.wmaker_icon)
692 {
693 XtMapWidget(top_level);
694 }
695 else if(ev->type==MapNotify)
696 {
697 if(AppData.wmaker_icon)
698 XWithdrawWindow(disp,XtWindow(top_level),DefaultScreen(disp));
699 else if(AppData.wm_icon)
700 XIconifyWindow(disp,XtWindow(top_level),DefaultScreen(disp));
701 }
702 }
703
VisibilityChange(Widget w,XtPointer closure,XEvent * ev,Boolean * cont)704 void VisibilityChange(Widget w,XtPointer closure,XEvent *ev,Boolean *cont)
705 {
706 (void)w; (void)closure;
707 *cont=True;
708
709 if(ev->type==VisibilityNotify
710 && ev->xvisibility.state!=VisibilityUnobscured
711 && AppData.alwaysOnTop && raise_tmout==-1)
712 {
713 raise_tmout=XtAppAddTimeOut(app_context,1000,
714 (XtTimerCallbackProc)RaiseButtonOnTimeout,NULL);
715 }
716 }
717
718 XtIntervalId pushed_tmout=-1;
drop_pushed()719 static void drop_pushed()
720 {
721 pushed_tmout=-1;
722 pushed=False;
723 }
PerformSwitch(void)724 static void PerformSwitch(void)
725 {
726 NewMode=(Mode^MODE)&~FOR_ONE;
727 FixNewMode();
728 SwitchKeyboard(NewMode);
729 pushed=True;
730 if(pushed_tmout!=-1)
731 XtRemoveTimeOut(pushed_tmout);
732 pushed_tmout=XtAppAddTimeOut(app_context,2000,
733 (XtTimerCallbackProc)drop_pushed,NULL);
734 }
735 #endif /* TK!=TK_NONE */
736
737 /* a window is in cache => we have requested all needed input from the window */
738 /* returns a boolean indicating the window was in cache
739 and adds it to cache if it was not */
AddToWindowCache(Window w)740 int AddToWindowCache(Window w)
741 {
742 time_t t=time(NULL);
743 time_t best_time=t;
744 int best_cell=-1;
745 int i;
746
747 for(i=0; i<XtNumber(window_cache); i++)
748 {
749 if(window_cache[i].win==w && window_cache[i].time!=0)
750 {
751 window_cache[i].time=t;
752 return(0);
753 }
754 if(best_cell==-1 || best_time>window_cache[i].time || window_cache[i].win==0)
755 {
756 best_cell=i;
757 best_time=window_cache[i].time;
758 }
759 }
760 window_cache[best_cell].time=t;
761 window_cache[best_cell].win=w;
762 return(1);
763 }
764
765 /* deletes a window from the cache */
DelWindow(Window w)766 void DelWindow(Window w)
767 {
768 int i;
769
770 for(i=0; i<XtNumber(window_cache); i++)
771 {
772 if(window_cache[i].win==w)
773 {
774 window_cache[i].win=0;
775 window_cache[i].time=0;
776 }
777 }
778 }
779
780 static void AddWindow(Window w,int tmout,int max_depth);
781
AddWindowOnTimeout(XtPointer closure,XtIntervalId * id)782 static void AddWindowOnTimeout(XtPointer closure,XtIntervalId *id)
783 {
784 (void)id;
785 DelWindow((Window)closure);
786 AddWindow((Window)closure,False,1000000);
787 }
788
XrusCheck(Window w)789 static void XrusCheck(Window w)
790 {
791 int class_len=strlen(AppClass);
792 XTextProperty prop;
793 unsigned char *zero;
794
795 if(!xrus_check)
796 return;
797
798 if(!XGetTextProperty(disp,w,&prop,XA_WM_CLASS))
799 return;
800
801 if(prop.format!=8)
802 return;
803
804 zero=memchr(prop.value,0,prop.nitems);
805 if(zero && zero+1+class_len<=prop.value+prop.nitems
806 && !memcmp(zero+1,AppClass,class_len)
807 && (zero+1+class_len==prop.value+prop.nitems
808 || zero[1+class_len]==0))
809 {
810 fprintf(stderr,"%s: xrus already runs on the display\n",program);
811 exit(1);
812 }
813 XFree(prop.value);
814 }
815
AddWindow(Window w,int tmout,int max_depth)816 static void AddWindow(Window w,int tmout,int max_depth)
817 {
818 XWindowAttributes wa;
819 long mask;
820 Window root1,parent,*children;
821 unsigned children_num,i;
822 Bool all_events_allowed=False;
823
824 if(max_depth<1)
825 return;
826
827 if(!AddToWindowCache(w)) /* if the window is in cache, skip it */
828 return;
829
830 XrusCheck(w);
831
832 if(!XQueryTree(disp,w,&root1,&parent,&children,&children_num))
833 {
834 DelWindow(w);
835 return;
836 }
837
838 if(w==focus_window)
839 all_events_allowed=True;
840
841 if(!all_events_allowed && !window_is_ours(w))
842 {
843 /* if it is not our own window, unselect any events,
844 so that all_event_masks has only event bits of other apps. */
845 /* ...unless it is possible to select any events. */
846 XSelectInput(disp,w,0);
847 }
848
849 if(!XGetWindowAttributes(disp,w,&wa))
850 {
851 DelWindow(w);
852 return;
853 }
854
855 mask=wa.your_event_mask;
856
857 /* we want to be notified of subwindows creation/destruction etc */
858 mask|=SubstructureNotifyMask;
859
860 /* keep track of focus window */
861 mask|=FocusChangeMask;
862
863 /* we request events only if
864 1. the window owner has already requested this kind of events
865 2. the window is blocking the events from propagation
866 3. parent window belongs to a different client.
867 */
868 mask|=(wa.all_event_masks|wa.do_not_propagate_mask)
869 &(KeyPressMask|KeyReleaseMask|PointerMotionMask);
870
871 if(all_events_allowed)
872 mask|=(KeyPressMask|KeyReleaseMask|PointerMotionMask|PropertyChangeMask);
873
874 /* we always want both Press and Release events */
875 if(mask&(KeyPressMask|KeyReleaseMask))
876 mask|=(KeyPressMask|KeyReleaseMask);
877
878 if(!(mask&KeyPressMask))
879 DelWindow(w); /* we'll check input mask later */
880
881 XSelectInput(disp,w,mask);
882
883 for(i=0; i<children_num; i++)
884 {
885 AddWindow(children[i],False,max_depth-1);
886 if(w==focus_window && window_is_top_level(children[i]))
887 {
888 last_top_level_focus_window=children[i];
889 last_top_level_revert_to=revert_to;
890 }
891 }
892 XFree(children);
893
894 if(tmout)
895 {
896 XtAppAddTimeOut(app_context,AppData.recheckTime,AddWindowOnTimeout,
897 (XtPointer)w);
898 }
899 }
900
SwitchKeysFire(void)901 void SwitchKeysFire(void)
902 {
903 NewMode^=MODE;
904 NewMode&=~FOR_ONE;
905 }
ToLatKeysFire(void)906 void ToLatKeysFire(void)
907 {
908 NewMode&=~MODE;
909 NewMode&=~FOR_ONE;
910 }
ToRusKeysFire(void)911 void ToRusKeysFire(void)
912 {
913 NewMode|=MODE;
914 NewMode&=~FOR_ONE;
915 }
SwitchForOneKeysFire(void)916 void SwitchForOneKeysFire(void)
917 {
918 NewMode^=FOR_ONE;
919 }
920
CheckKeymap()921 void CheckKeymap()
922 {
923 static int first_time=1;
924
925 int ok_rus=0;
926 int ok_lat=0;
927 int i,groupwidth;
928 KeySym *keymap=GetKeymap();
929
930 if(keymap==0)
931 {
932 Mode=LAT;
933 exit(1);
934 }
935
936 if(keysyms_per_keycode>=4)
937 {
938 for(i=(max_keycode-min_keycode)*keysyms_per_keycode;
939 i>=0; i-=keysyms_per_keycode)
940 {
941 if((groupwidth=SwappableKey(keymap+i)) > 0)
942 {
943 ok_lat+=(NationalKeySym(keymap[i+groupwidth]) && NationalKeySym(keymap[i+groupwidth+1]));
944 ok_rus+=(NationalKeySym(keymap[i+0]) && NationalKeySym(keymap[i+1]));
945 }
946 }
947 }
948
949 if(ok_rus==ok_lat)
950 {
951 fprintf(stderr,"%s: key mapping is not in proper coding, exiting.\n",program);
952 if(first_time)
953 {
954 fprintf(stderr," You might probably want to load a localized modmap before starting xrus.\n"
955 " Alternatively, you can specify modmap file name on the command line,\n"
956 " like this: ./xrus jcuken-koi8.xmm\n");
957 }
958 else
959 {
960 fprintf(stderr," This means your modmap has changed to single-mode one.\n"
961 " If you were doing xmodmap <file> at the time, check your mapping in\n"
962 " the file. For normal xrus work it is required that modmap has both\n"
963 " national and latin letters\n");
964 }
965 Mode=LAT;
966 exit(1);
967 }
968
969 NewMode=Mode=(Mode&~(MODE))|(ok_rus>ok_lat?RUS:LAT);
970 FixNewMode();
971 SwitchKeyboard(NewMode);
972
973 ShowSwitchButton();
974
975 first_time=0;
976 }
977
978 static Window set_focus;
979 static int set_revert_to;
980 XtIntervalId focus_tmout=-1;
SetFocusOnTimeout()981 static void SetFocusOnTimeout()
982 {
983 if(set_focus && pushed)
984 XSetInputFocus(disp,set_focus,set_revert_to,CurrentTime);
985 focus_tmout=-1;
986 }
NewFocus(Window w,int rt)987 static void NewFocus(Window w,int rt)
988 {
989 if(w==focus_window)
990 return;
991 if((top_level && w==XtWindow(top_level)) || w==our_host_window)
992 {
993 if(focus_tmout!=-1)
994 XtRemoveTimeOut(focus_tmout);
995 set_focus=last_top_level_focus_window;
996 set_revert_to=last_top_level_revert_to;
997 focus_tmout=XtAppAddTimeOut(app_context,200,
998 (XtTimerCallbackProc)SetFocusOnTimeout,NULL);
999 return;
1000 }
1001 if(window_is_top_level(w))
1002 {
1003 last_top_level_focus_window=w;
1004 last_top_level_revert_to=rt;
1005 if(AppData.per_window_state)
1006 SwitchKeyboardForWindow(w);
1007 }
1008 focus_window=w;
1009 revert_to=rt;
1010 DelWindow(w);
1011 AddWindow(w,False,1);
1012 }
1013
QueryFocus()1014 static void QueryFocus()
1015 {
1016 Window w=0;
1017 int revert_to=0;
1018 XGetInputFocus(disp,&w,&revert_to);
1019 if(w)
1020 NewFocus(w,revert_to);
1021 }
1022
1023 static
MappingNotifyTimeoutHandler(XtPointer closure,XtIntervalId * id)1024 void MappingNotifyTimeoutHandler(XtPointer closure,XtIntervalId *id)
1025 {
1026 (void)closure; (void)id;
1027 MappingNotifyTimeout=-1;
1028 CheckKeymap();
1029 if(focus_window==0)
1030 QueryFocus();
1031 }
1032
LogKeyStr(const char * string,int len)1033 void LogKeyStr(const char *string,int len)
1034 {
1035 const char *nl;
1036
1037 if(len==-1)
1038 len=strlen(string);
1039
1040 nl=memchr(string,'\n',len);
1041 if(nl)
1042 keylog_pos=len-(nl-string+1);
1043 else
1044 keylog_pos+=len;
1045
1046 if(keylog_pos>75)
1047 {
1048 write(keylog_fd,"\n",1);
1049 keylog_pos=nl?len-(nl-string+1):len;
1050 }
1051
1052 write(keylog_fd,string,len);
1053 }
1054
LogKey(KeySym ks,char * string,int len)1055 void LogKey(KeySym ks,char *string,int len)
1056 {
1057 time_t t;
1058
1059 if(keylog_fd==-1)
1060 return;
1061
1062 if(len>0)
1063 {
1064 if(string[0]=='\r')
1065 {
1066 strcpy(string,"<CR>\n");
1067 len=-1;
1068 }
1069 else if((unsigned char)string[0] < 32)
1070 {
1071 sprintf(string,"^%c",string[0]+'@');
1072 len=2;
1073 }
1074 }
1075 else
1076 {
1077 sprintf(string,"<%.60s>",XKeysymToString(ks));
1078 len=-1;
1079 }
1080 if(time(&t)-keylog_time>=60)
1081 {
1082 char ts[64];
1083 strftime(ts,sizeof(ts),"<%Y-%m-%d %H:%M:%S>",localtime(&t));
1084 LogKeyStr(ts,-1);
1085 keylog_time=t;
1086 }
1087 LogKeyStr(string,len);
1088 }
1089
MainLoop(void)1090 void MainLoop(void)
1091 {
1092 XEvent ev;
1093 KeySym ks;
1094 int was_caps_locked;
1095 Bool fired;
1096 int len;
1097 char string[64];
1098
1099 for(;;)
1100 {
1101 alloca(0); /* garbage collection */
1102
1103 if(app_context==NULL)
1104 return;
1105 XtAppNextEvent(app_context,&ev);
1106
1107 was_caps_locked=IsKeySymPressed(XK_Caps_Lock);
1108
1109 XtDispatchEvent(&ev);
1110
1111 KeepTrackOfKeyboard(&ev);
1112
1113 if(NewMode!=Mode)
1114 {
1115 FixNewMode();
1116 SwitchKeyboard(NewMode);
1117 }
1118
1119 switch(ev.type)
1120 {
1121 case(MapNotify):
1122 AddWindow(ev.xmap.window,True,1000000);
1123 QueryFocus();
1124 break;
1125 case(ReparentNotify):
1126 /* the structure could change under us - re-add the window */
1127 AddWindow(ev.xreparent.window,True,1000000);
1128 if(ev.xreparent.window==XtWindow(top_level))
1129 our_host_window=ev.xreparent.parent;
1130 break;
1131 case(DestroyNotify):
1132 DelWindow(ev.xdestroywindow.window);
1133 break;
1134 case(KeyPress):
1135 SetAlarm();
1136
1137 ks=XLookupKeysym(&ev.xkey,0);
1138 len=XLookupString(&ev.xkey,string,sizeof(string),0,0);
1139
1140 NewMode=Mode;
1141
1142 if(ks==XK_Caps_Lock && !was_caps_locked)
1143 NewMode^=CAPSLOCK_ON;
1144
1145 KeyCombinationProcessKeyPress(&SwitchKeys,ks);
1146 KeyCombinationProcessKeyPress(&ToRusKeys,ks);
1147 KeyCombinationProcessKeyPress(&ToLatKeys,ks);
1148 KeyCombinationProcessKeyPress(&SwitchForOneKeys,ks);
1149
1150 if(len>0)
1151 NewMode&=~FOR_ONE;
1152
1153 LogKey(ks,string,len);
1154
1155 FixNewMode();
1156 SwitchKeyboard(NewMode);
1157
1158 break;
1159 case(KeyRelease):
1160 SetAlarm();
1161
1162 ks=XLookupKeysym(&ev.xkey,0);
1163
1164 NewMode=Mode;
1165
1166 fired=False;
1167 fired|=KeyCombinationProcessKeyRelease(&SwitchKeys,ks,!fired);
1168 fired|=KeyCombinationProcessKeyRelease(&ToLatKeys,ks,!fired);
1169 fired|=KeyCombinationProcessKeyRelease(&ToRusKeys,ks,!fired);
1170 fired|=KeyCombinationProcessKeyRelease(&SwitchForOneKeys,ks,!fired);
1171
1172 FixNewMode();
1173 SwitchKeyboard(NewMode);
1174
1175 break;
1176 case(MotionNotify):
1177 case(ButtonPress):
1178 case(ButtonRelease):
1179 SetAlarm();
1180 break;
1181 case(MappingNotify):
1182 if(ev.xmapping.request==MappingKeyboard)
1183 {
1184 /* workaround with timeout for xmodmap bug (too many events) */
1185 if(MappingNotifyIgnoreCount==0)
1186 {
1187 if(MappingNotifyTimeout!=-1)
1188 XtRemoveTimeOut(MappingNotifyTimeout);
1189 MappingNotifyTimeout=XtAppAddTimeOut(app_context,200,
1190 MappingNotifyTimeoutHandler,NULL);
1191 }
1192 else
1193 {
1194 MappingNotifyIgnoreCount--;
1195 if(MappingNotifyIgnoreCount==0 && load_map!=0)
1196 {
1197 run_xmodmap(load_map);
1198 load_map=0;
1199 }
1200 }
1201 }
1202 break;
1203 case(FocusIn):
1204 QueryFocus();
1205 break;
1206 case(FocusOut):
1207 {
1208 Window old=focus_window;
1209 focus_window=0;
1210 DelWindow(ev.xany.window);
1211 AddWindow(ev.xany.window,False,1);
1212 focus_window=old;
1213 break;
1214 }
1215 case(PropertyNotify):
1216 if(AppData.per_window_state && ev.xproperty.atom==XA_WM_NAME
1217 && ev.xproperty.state==PropertyNewValue)
1218 {
1219 if(SetTitleTimeout!=-1)
1220 XtRemoveTimeOut(SetTitleTimeout);
1221 SetTitleTimeout=XtAppAddTimeOut(app_context,1000,
1222 (XtTimerCallbackProc)SetTitleIndicatorOnTimeout,
1223 (XtPointer)ev.xproperty.window);
1224 }
1225 break;
1226 #if TK==TK_XAW
1227 case(ClientMessage):
1228 fflush(stdout);
1229 if(ev.xclient.message_type==wm_protocols && ev.xclient.format==32
1230 && ev.xclient.data.l[0]==wm_delete_window)
1231 {
1232 if(ev.xclient.window==XtWindow(top_level))
1233 {
1234 XtDestroyApplicationContext(app_context);
1235 app_context=NULL;
1236 }
1237 else
1238 {
1239 Widget w=XtWindowToWidget(disp,ev.xclient.window);
1240 if(w)
1241 XtPopdown(w);
1242 }
1243 }
1244 break;
1245 #endif /* TK_XAW */
1246 }
1247 }
1248 }
1249
cleanup(void)1250 void cleanup(void)
1251 {
1252 if(app_context)
1253 SwitchKeyboard(LAT);
1254 }
1255
sig_term(int sig)1256 void sig_term(int sig)
1257 {
1258 fprintf(stderr,"%s: caught signal %d, exiting\n",program,sig);
1259 cleanup();
1260 exit(1);
1261 }
1262
sig_chld(int sig)1263 void sig_chld(int sig)
1264 {
1265 int status;
1266 if(wait(&status)==LockerRunning)
1267 {
1268 LockerRunning=0;
1269 SetAlarm();
1270 }
1271 }
1272
LockScreen()1273 void LockScreen()
1274 {
1275 pid_t pid;
1276 sigset_t nset;
1277
1278 SwitchKeyboard(Mode&~MODE);
1279
1280 fflush(stderr);
1281 switch(pid=fork())
1282 {
1283 case(0):
1284 sigemptyset (&nset);
1285 sigprocmask (SIG_SETMASK, &nset, NULL);
1286 execl("/bin/sh","sh","-c",AppData.locker,NULL);
1287 fprintf(stderr,"%s: cannot execute /bin/sh - %s\n",program,strerror(errno));
1288 fflush(stderr);
1289 _exit(1);
1290 case(-1):
1291 fprintf(stderr,"%s: cannot do fork() - %s\n",program,strerror(errno));
1292 fflush(stderr);
1293 break;
1294 default:
1295 LockerRunning=pid;
1296 }
1297 }
1298
run_xmodmap(const char * file)1299 void run_xmodmap(const char *file)
1300 {
1301 char *alt_file;
1302 char *xmodmap=AppData.xmodmap_program;
1303
1304 XSync(disp,False);
1305
1306 try_again:
1307 if(access(file,R_OK)==-1)
1308 {
1309 if(file[0]!='/')
1310 {
1311 alt_file=alloca(strlen(KEYMAP_DIR)+strlen(file)+2+4);
1312 sprintf(alt_file,"%s/%s",KEYMAP_DIR,file);
1313 if(access(alt_file,R_OK)==0)
1314 file=alt_file;
1315 else
1316 {
1317 strcat(alt_file,".xmm");
1318 if(access(alt_file,R_OK)==0)
1319 file=alt_file;
1320 else
1321 {
1322 /* backward compatibility */
1323 char *x=strstr(file,"-xrus");
1324 if(x)
1325 {
1326 memmove(x,x+4,strlen(x+4)+1);
1327 goto try_again;
1328 }
1329 }
1330 }
1331 }
1332 }
1333
1334 fflush(stderr);
1335 switch(fork())
1336 {
1337 case(-1):
1338 fprintf(stderr,"%s: fork() failed - %s\n",program,strerror(errno));
1339 break;
1340 case(0):
1341 /* child */
1342 execlp(xmodmap,xmodmap,file,NULL);
1343 fprintf(stderr,"%s: execlp(%s) failed - %s\n",program,xmodmap,strerror(errno));
1344 fflush(stderr);
1345 _exit(1);
1346 default:
1347 /* parent */
1348 ;
1349 }
1350 }
1351
AlarmHandler(int sig)1352 void AlarmHandler(int sig)
1353 {
1354 time_t now=time(0);
1355 if(now < alarm_expected)
1356 {
1357 fprintf(stderr,"%s: unexpected early alarm signal\n",program);
1358 alarm(alarm_expected-now);
1359 return;
1360 }
1361 if(AppData.autolock)
1362 {
1363 #ifdef DEBUG
1364 printf("calling LockScreen() at %ld\n",now);
1365 #endif
1366 LockScreen();
1367 }
1368 (void)sig;
1369 }
1370
SetAlarm()1371 void SetAlarm()
1372 {
1373 alarm(0);
1374 if(AppData.autolock && AppData.timeout>0 && !LockerRunning)
1375 {
1376 alarm(AppData.timeout*60);
1377 alarm_expected=time(0)+AppData.timeout*60-1;
1378 #ifdef DEBUG
1379 printf("alarm expected at %ld\n",alarm_expected);
1380 #endif
1381 }
1382 }
1383
X_error_handler(Display * d,XErrorEvent * ev)1384 int X_error_handler(Display *d,XErrorEvent *ev)
1385 {
1386 /* A window can be deleted between CreateNotify and our request */
1387
1388 if(ev->error_code==BadWindow
1389 || ev->error_code==BadDrawable
1390 || ev->error_code==BadAccess)
1391 return(-1);
1392 return(old_error_handler(d,ev));
1393 }
1394
hook_signals(void)1395 void hook_signals(void)
1396 {
1397 struct sigaction act;
1398
1399 act.sa_handler=sig_term;
1400 sigemptyset(&act.sa_mask);
1401 act.sa_flags=0;
1402 sigaction(SIGTERM,&act,NULL);
1403 sigaction(SIGINT,&act,NULL);
1404 sigaction(SIGQUIT,&act,NULL);
1405
1406 act.sa_handler=SIG_IGN;
1407 sigaction(SIGHUP,&act,NULL);
1408
1409 act.sa_handler=AlarmHandler;
1410 act.sa_flags=SA_RESTART;
1411 sigaction(SIGALRM,&act,NULL);
1412 act.sa_handler=sig_chld;
1413 act.sa_flags=SA_RESTART|SA_NOCLDSTOP;
1414 sigaction(SIGCHLD,&act,NULL);
1415
1416 act.sa_handler=ToLatKeysFire;
1417 act.sa_flags=0;
1418 sigaction(SIGUSR1,&act,NULL);
1419 act.sa_handler=ToRusKeysFire;
1420 act.sa_flags=0;
1421 sigaction(SIGUSR2,&act,NULL);
1422 }
1423
main(int argc,char ** argv)1424 int main(int argc,char **argv)
1425 {
1426 Atom ol_decor_del_atom,ol_decor_del[2];
1427 XrmDatabase default_rdb,rdb;
1428 char *HOME;
1429 char *buf;
1430 char err[1024];
1431 int scr;
1432
1433 Window w;
1434 Atom wm_client_leader;
1435
1436 int saved_argc=argc;
1437 char **saved_argv=alloca((argc+1)*sizeof(char*));
1438 memcpy(saved_argv,argv,argc*sizeof(char*));
1439 saved_argv[argc]=0;
1440
1441 program=argv[0];
1442
1443 if(argc==2)
1444 {
1445 if(!strcmp(argv[1],"--version")
1446 || !strcmp(argv[1],"-version")
1447 || !strcmp(argv[1],"-v"))
1448 {
1449 printf("Xrus(keyboard switcher) version " VERSION "\n"
1450 "\n"
1451 "Copyright (c) 1995-2001 Alexander V. Lukyanov (lav@yars.free.net)\n"
1452 "This is free software that gives you freedom to use, modify and distribute\n"
1453 "it under certain conditions, see COPYING (GNU GPL) for details.\n"
1454 "There is ABSOLUTELY NO WARRANTY, use at your own risk.\n");
1455 exit(0);
1456 }
1457 if(!strcmp(argv[1],"--help")
1458 || !strcmp(argv[1],"-help")
1459 || !strcmp(argv[1],"-h")
1460 || !strcmp(argv[1],"-?"))
1461 {
1462 printf("Usage: %s [OPTIONS] [keymap_file]\n"
1463 "\n"
1464 " {-|+}autolock turn on/off screen autolocking\n"
1465 " -locker \"locker cmd\" specify locker command\n"
1466 " -timeout sec set autolock timeout\n"
1467 " {-|+}bell turn on/off bell on keyboard switch\n"
1468 " {-|+}icon turn on/off indicator icon\n"
1469 " -led led_no use the led to indicate alternative mode\n"
1470 " +led don't use led for indication\n"
1471 " -nofork don't fork to background\n"
1472 " -wmaker display in Window Maker appicon\n"
1473 " -wmicon display in window manager icon\n"
1474 " -perwindow remember state for each window\n"
1475 "\n",program);
1476 exit(0);
1477 }
1478 }
1479
1480 hook_signals();
1481
1482 XtToolkitInitialize();
1483 XtSetLanguageProc(NULL,NULL,NULL);
1484 app_context=XtCreateApplicationContext();
1485 disp=XtOpenDisplay(app_context,NULL,"xrus",AppClass,options,XtNumber(options),&argc,argv);
1486 if(disp==NULL)
1487 {
1488 fprintf(stderr,"%s: cannot open display\n",program);
1489 exit(1);
1490 }
1491 client_id_mask=~(disp->resource_mask);
1492
1493 if(argc>1 && argv[1][0]=='-')
1494 {
1495 fprintf(stderr,"%s: invalid option -- %s\n",program,argv[1]);
1496 fprintf(stderr,"%s: Try `%s --help' for more information\n",program,program);
1497 exit(1);
1498 }
1499
1500 default_rdb=XrmGetStringDatabase(DefaultResources);
1501 rdb=XtDatabase(disp);
1502 XrmCombineDatabase(default_rdb,&rdb,False);
1503 HOME=getenv("HOME");
1504 if(HOME)
1505 {
1506 buf=alloca(strlen(HOME)+10);
1507 sprintf(buf,"%s/.xrus",HOME);
1508 XrmCombineFileDatabase(buf,&rdb,True);
1509 }
1510
1511 old_error_handler=XSetErrorHandler(X_error_handler);
1512
1513 /* select events from all windows located on the display
1514 and check for another xrus instance */
1515 for(scr=0; scr<ScreenCount(disp); scr++)
1516 AddWindow(RootWindowOfScreen(ScreenOfDisplay(disp,scr)),True,1000000);
1517 xrus_check=0;
1518
1519 StartArgs();
1520 #if TK==TK_MOTIF
1521 AddArg(XmNmwmDecorations,MWM_DECOR_BORDER);
1522 AddArg(XmNmwmFunctions,MWM_FUNC_MOVE|MWM_FUNC_CLOSE);
1523 #elif TK==TK_NONE
1524 AddArg(XtNwidth,10); /* no toolkit - set size to avoid 0x0 size */
1525 AddArg(XtNheight,10);
1526 #endif
1527 AddArg(XtNinput,False);
1528 AddArg(XtNallowShellResize,True);
1529 AddArg(XtNmappedWhenManaged,False);
1530 AddArg(XtNargc,saved_argc);
1531 AddArg(XtNargv,saved_argv);
1532 top_level=XtAppCreateShell("xrus",AppClass,applicationShellWidgetClass,disp,args,count);
1533
1534 XtGetApplicationResources(top_level,&AppData,resources,XtNumber(resources),NULL,0);
1535
1536 SetTitle(0);
1537
1538 /* replace pointer to constant string with pointer to our buffers */
1539 strcpy(LockerData,AppData.locker?AppData.locker:DefaultLocker);
1540 AppData.locker=LockerData;
1541 strcpy(SwitchKeysData,AppData.switchKeys?AppData.switchKeys:DefaultSwitchKeys);
1542 AppData.switchKeys=SwitchKeysData;
1543 strcpy(ToLatKeysData,AppData.toLatKeys?AppData.toLatKeys:DefaultToLatKeys);
1544 AppData.toLatKeys=ToLatKeysData;
1545 strcpy(ToRusKeysData,AppData.toRusKeys?AppData.toRusKeys:DefaultToRusKeys);
1546 AppData.toRusKeys=ToRusKeysData;
1547 strcpy(SwitchForOneKeysData,AppData.switchForOneKeys?AppData.switchForOneKeys:DefaultSwitchForOneKeys);
1548 AppData.switchForOneKeys=SwitchForOneKeysData;
1549
1550 if(argc>1)
1551 {
1552 AppData.xmodmap=argv[1];
1553 }
1554
1555 if(AppData.xmodmap[0])
1556 {
1557 run_xmodmap(AppData.xmodmap);
1558 }
1559
1560 /* let the keymap to settle */
1561 MappingNotifyTimeout=XtAppAddTimeOut(app_context,500,
1562 MappingNotifyTimeoutHandler,NULL);
1563
1564 /* initialize switch keys */
1565 SwitchKeys.Fire=SwitchKeysFire;
1566 ParseKeyCombination(AppData.switchKeys,&SwitchKeys,err);
1567 PrintParseErrors("switchKeys",err);
1568 ToLatKeys.Fire=ToLatKeysFire;
1569 ParseKeyCombination(AppData.toLatKeys,&ToLatKeys,err);
1570 PrintParseErrors("toLatKeys",err);
1571 ToRusKeys.Fire=ToRusKeysFire;
1572 ParseKeyCombination(AppData.toRusKeys,&ToRusKeys,err);
1573 PrintParseErrors("toRusKeys",err);
1574 SwitchForOneKeys.Fire=SwitchForOneKeysFire;
1575 ParseKeyCombination(AppData.switchForOneKeys,&SwitchForOneKeys,err);
1576 PrintParseErrors("switchForOneKeys",err);
1577
1578 if(AppData.keylog_file)
1579 {
1580 keylog_fd=open(AppData.keylog_file,O_WRONLY|O_APPEND|O_CREAT,0600);
1581 if(keylog_fd==-1)
1582 perror(AppData.keylog_file);
1583 }
1584
1585 #if !defined(DEBUG) && !defined(DONT_FORK)
1586 if(!AppData.noFork && !AppData.wmaker_icon)
1587 {
1588 switch(fork())
1589 {
1590 case(0):
1591 break;
1592 case(-1):
1593 perror("fork");
1594 exit(1);
1595 default:
1596 _exit(0);
1597 }
1598 }
1599 #endif
1600
1601 #ifdef HAVE_ATEXIT
1602 atexit(cleanup);
1603 #else
1604 # ifdef __sun__
1605 on_exit(cleanup,NULL); /* non-ANSI exit handler */
1606 # endif
1607 #endif
1608
1609 XtAppAddActions(app_context,Actions,XtNumber(Actions));
1610
1611 #if TK!=TK_NONE
1612 XtAddEventHandler(top_level,StructureNotifyMask,False,UnmapHandler,NULL);
1613
1614 # if TK==TK_MOTIF
1615 StartArgs();
1616 AddArg(XmNresizable,False);
1617 form_w=XtCreateManagedWidget("form",xmFormWidgetClass,top_level,args,count);
1618
1619 switch_button[0]=XtCreateWidget("modeButton0",xmPushButtonWidgetClass,form_w,NULL,0);
1620 switch_button[1]=XtCreateWidget("modeButton1",xmPushButtonWidgetClass,form_w,NULL,0);
1621
1622 XtAddCallback(switch_button[0],XmNactivateCallback,(XtCallbackProc)PerformSwitch,NULL);
1623 XtAddCallback(switch_button[1],XmNactivateCallback,(XtCallbackProc)PerformSwitch,NULL);
1624 # elif TK==TK_XAW
1625 form_w=XtCreateManagedWidget("form",boxWidgetClass,top_level,NULL,0);
1626
1627 switch_button[0]=XtCreateWidget("modeButton0",commandWidgetClass,form_w,NULL,0);
1628 switch_button[1]=XtCreateWidget("modeButton1",commandWidgetClass,form_w,NULL,0);
1629
1630 XtAddCallback(switch_button[0],XtNcallback,(XtCallbackProc)PerformSwitch,NULL);
1631 XtAddCallback(switch_button[1],XtNcallback,(XtCallbackProc)PerformSwitch,NULL);
1632 # else
1633 # error unknown toolkit
1634 # endif
1635
1636 XtManageChild(switch_button[(Mode&MODE)==RUS]);
1637
1638 XtAddEventHandler(switch_button[0],VisibilityChangeMask,False,VisibilityChange,NULL);
1639 XtAddEventHandler(switch_button[1],VisibilityChangeMask,False,VisibilityChange,NULL);
1640
1641 XrusMenuCreate();
1642
1643 #endif /* !TK_NONE */
1644
1645 kbd_state_atom=XInternAtom(disp,"KBD_STATE",False);
1646
1647 XtRealizeWidget(top_level);
1648
1649 KeyboardStateInit();
1650 InitCapsLockEmu();
1651
1652 #if TK!=TK_NONE
1653 /* Set various window properties. */
1654 w=XtWindow(top_level);
1655
1656 ol_decor_del_atom=XInternAtom(disp,"_OL_DECOR_DEL",False);
1657 ol_decor_del[0]=XInternAtom(disp,"_OL_DECOR_HEADER",False);
1658 ol_decor_del[1]=XInternAtom(disp,"_OL_DECOR_RESIZE",False);
1659
1660 wm_client_leader=XInternAtom(disp,"WM_CLIENT_LEADER",False);
1661 wm_delete_window=XInternAtom(disp,"WM_DELETE_WINDOW",False);
1662 wm_protocols=XInternAtom(disp,"WM_PROTOCOLS",False);
1663
1664 XChangeProperty(disp,w,ol_decor_del_atom,XA_ATOM,32,
1665 PropModeReplace,(void*)ol_decor_del,2);
1666
1667 XChangeProperty(disp,w,wm_client_leader,XA_WINDOW,32,
1668 PropModeReplace,(void*)&w,1);
1669
1670 #define WinStateAllWorkspaces (1 << 0) /* appears on all workspaces */
1671 #define WinStateMinimized (1 << 1) /* to iconbox,taskbar,... */
1672 #define WinStateMaximizedVert (1 << 2) /* maximized vertically */
1673 #define WinStateMaximizedHoriz (1 << 3) /* maximized horizontally */
1674 #define WinStateHidden (1 << 4) /* not on taskbar if any, but still accessible */
1675 #define WinStateRollup (1 << 5) /* only titlebar visible */
1676 #define WinStateFixedPosition (1 << 10) /* fixed position on virtual desktop*/
1677 #define WinStateArrangeIgnore (1 << 11) /* ignore for auto arranging */
1678 #define WinStateWithdrawn (1 << 31) /* managed, but not available to user */
1679
1680 if(AppData.occupyAllDesks)
1681 { /* make the window global for all desktops */
1682 Atom win_state,sgi_desks_hints,sgi_desks_always_global;
1683 Atom kwm_win_sticky;
1684 long value;
1685 /* for gnome */
1686 static long state[2]={WinStateAllWorkspaces|WinStateArrangeIgnore,63};
1687 win_state=XInternAtom(disp,"_WIN_STATE",False);
1688 XChangeProperty(disp,w,win_state,XA_CARDINAL,32,
1689 PropModeReplace,(void*)state,2);
1690 /* for SGI */
1691 sgi_desks_hints=XInternAtom(disp,"_SGI_DESKS_HINTS",False);
1692 sgi_desks_always_global=XInternAtom(disp,"_SGI_DESKS_ALWAYS_GLOBAL",False);
1693 value=sgi_desks_always_global;
1694 XChangeProperty(disp,w,sgi_desks_hints,XA_ATOM,32,
1695 PropModeReplace,(void*)&value,1);
1696 /* for KDE */
1697 value=1;
1698 kwm_win_sticky=XInternAtom(disp,"KWM_WIN_STICKY",False);
1699 XChangeProperty(disp,w,kwm_win_sticky,kwm_win_sticky,32,
1700 PropModeReplace,(void*)&value,1);
1701 }
1702
1703 #define WinHintsSkipFocus (1 << 0)
1704 #define WinHintsSkipWindowMenu (1 << 1)
1705 #define WinHintsSkipTaskBar (1 << 2)
1706 #define WinHintsGroupTransient (1 << 3)
1707 #define WinHintsDockHorizontal (1 << 6) /* docked horizontally */
1708
1709 {
1710 Atom win_hints=XInternAtom(disp,"_WIN_HINTS",False);
1711 long value[2];
1712 value[0]=WinHintsSkipFocus|WinHintsSkipWindowMenu;
1713 if(!AppData.wm_icon)
1714 value[0]|=WinHintsSkipTaskBar;
1715 value[1]=127;
1716 XChangeProperty(disp,w,win_hints,XA_CARDINAL,32,
1717 PropModeReplace,(void*)value,2);
1718 }
1719
1720 #if TK!=TK_MOTIF
1721 {
1722 /* we have to emulate motif hints as we don't have Motif */
1723 static long motif_hints[]={0x3, 0x24, 0x2, 0xffffffff, 0};
1724 Atom motif_hints_atom=XInternAtom(disp,"_MOTIF_WM_HINTS",False);
1725 XChangeProperty(disp,XtWindow(top_level),motif_hints_atom,motif_hints_atom,32,
1726 PropModeReplace,(void*)motif_hints,XtNumber(motif_hints));
1727
1728 /* Xaw' shell don't catches WM_DELETE_WINDOW, have to do it myself */
1729 XSetWMProtocols(disp,XtWindow(top_level),&wm_delete_window,1);
1730 }
1731 #endif
1732
1733 if(AppData.adjustModeButtons)
1734 {
1735 Dimension w0,h0,w1,h1;
1736
1737 StartArgs();
1738 AddArg(XtNwidth,&w0);
1739 AddArg(XtNheight,&h0);
1740 XtGetValues(switch_button[0],args,count);
1741
1742 StartArgs();
1743 AddArg(XtNwidth,&w1);
1744 AddArg(XtNheight,&h1);
1745 XtGetValues(switch_button[1],args,count);
1746
1747 if(w0<w1)
1748 w0=w1;
1749 if(h0<h1)
1750 h0=h1;
1751
1752 StartArgs();
1753 AddArg(XtNwidth,w0);
1754 AddArg(XtNheight,h0);
1755 XtSetValues(switch_button[0],args,count);
1756 XtSetValues(switch_button[1],args,count);
1757 XtSetValues(form_w,args,count);
1758 }
1759
1760 if(AppData.wm_icon || AppData.wmaker_icon)
1761 {
1762 int state=IconicState;
1763 if(AppData.wmaker_icon)
1764 state=WithdrawnState;
1765 XtUnmapWidget(form_w);
1766 XReparentWindow(disp,XtWindow(form_w),RootWindowOfScreen(ScreenOfDisplay(disp,scr)),0,0);
1767 XtVaSetValues(top_level,
1768 XtNiconWindow,XtWindow(form_w),
1769 XtNinitialState,state,
1770 NULL);
1771 XtMapWidget(top_level);
1772 }
1773 else
1774 {
1775 if(AppData.icon)
1776 XtMapWidget(top_level);
1777 }
1778 #endif /* TK!=TK_NONE */
1779
1780 SetAlarm();
1781 MainLoop();
1782
1783 cleanup();
1784 return 0;
1785 }
1786
1787 #ifdef C_ALLOCA
1788 /* alloca.c calls xmalloc */
xmalloc(size_t s)1789 void *xmalloc(size_t s)
1790 {
1791 void *p=malloc(s);
1792 if(p)
1793 return(p);
1794 fprintf(stderr,"%s: out of virtual memory\n",program);
1795 cleanup();
1796 exit(1);
1797 }
1798 #endif /* C_ALLOCA */
1799