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