1 /* Copyright (C) 1999 Rafal Wierzbicki <rafal@mcss.mcmaster.ca>
2 
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16  *
17  *
18  * TODO:
19  * State Toggles:
20  *                      Fixed Position
21  *                      Ignore on Arrange
22  *
23  * no idea what this is:
24  * EXPANDED_SIZE
25  */
26 
27 #undef DEBUG
28 static char *cvsident = "$Id: Gnome.c,v 1.5 2009/02/11 23:32:03 sasha Exp $";
29 
30 #include <X11/Intrinsic.h>
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
33 #include <X11/Xos.h>
34 #include <X11/Xatom.h>
35 #include <X11/Xproto.h>
36 #include <X11/cursorfont.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39 #include <stdio.h>
40 #include <signal.h>
41 #include <unistd.h>
42 #include <errno.h>
43 #include "../../configure.h"
44 #include "../../include/aftersteplib.h"
45 #include "../../include/afterstep.h"
46 #include "../../include/module.h"
47 
48 #include "Gnome.h"
49 
50 #ifdef DEBUG
51 #define LOG(s) fprintf (stderr, "%s\n", s)
52 #else
53 #define LOG(s)
54 #endif
55 
56 static s_list *window_list;	/* single linked list of window id's etc */
57 static int current_desk = 0;	/* current workspace */
58 static char **desk_names;	/* holds workspace names */
59 static int desk_count = 0;	/* number of workspaces */
60 static Bool PAGES = False;	/* virtual paging or no */
61 static int area_x = 0;		/* area coordinates for virtual paging */
62 static int area_y = 0;		/*                ""                   */
63 
64 
65 ScreenInfo Scr;
66 
67 /* inserts a window id into the list
68  * args: list, id
69  */
70 list_item *
s_list_insert(s_list * list,long id)71 s_list_insert (s_list * list, long id)
72 {
73   list_item *item = NULL;
74 
75   item = (list_item *) safemalloc (sizeof (list_item));
76 
77   if (!item)
78     return NULL;
79 
80   if (list->first == NULL)
81     {
82       list->first = item;
83       list->last = item;
84     }
85   else
86     {
87       list->last->next = item;
88     }
89   item->next = NULL;
90   list->last = item;
91   item->id = id;
92   return item;
93 }
94 
95 /* removes an item from list by window id
96  * args: list, id
97  */
98 int
s_list_remove_by_data(s_list * list,long id)99 s_list_remove_by_data (s_list * list, long id)
100 {
101   list_item *tmp1;
102 
103   tmp1 = s_list_find_by_data (list, id);
104 
105   if (tmp1 == NULL)
106     return 0;
107 
108   /* this is our only list item */
109   if (tmp1 == list->first && tmp1 == list->last)
110     {
111       free (tmp1);
112       list->first = NULL;
113       list->last = NULL;
114       return 1;
115     }
116   /* the last item */
117   else if (tmp1 == list->last)
118     {
119       list_item *tmp2;
120 
121       tmp2 = list->first;
122       while (tmp2)
123 	{
124 	  if (tmp2->next == list->last)
125 	    break;
126 	  tmp2 = tmp2->next;
127 	}
128       free (tmp1);
129       tmp2->next = NULL;
130       list->last = tmp2;
131       return 1;
132     }
133   /* the first item */
134   else if (tmp1 == list->first)
135     {
136       list->first = tmp1->next;
137       free (tmp1);
138       return 1;
139     }
140   /*somewhere in between */
141   else
142     {
143       list_item *tmp2;
144 
145       tmp2 = list->first;
146       while (tmp2)
147 	{
148 	  if (tmp2->next == tmp1)
149 	    break;
150 	  tmp2 = tmp2->next;
151 	}
152       tmp2->next = tmp1->next;
153       free (tmp1);
154       return 1;
155     }
156   return 0;
157 }
158 
159 /* searches for a window id in a list
160  * args: list, id
161  */
162 list_item *
s_list_find_by_data(s_list * list,long id)163 s_list_find_by_data (s_list * list, long id)
164 {
165   list_item *tmp;
166 
167   tmp = list->first;
168 
169   if (list->first == NULL && list->last == NULL)
170     return NULL;
171 
172   while (tmp)
173     {
174       if (tmp->id == id)
175 	return tmp;
176       tmp = tmp->next;
177     }
178   return NULL;
179 }
180 
181 /* creates a new single linked list */
182 s_list *
s_list_new()183 s_list_new ()
184 {
185   s_list *list;
186 
187   list = (s_list *) safemalloc (sizeof (s_list));
188 
189   if (!list)
190     return NULL;
191 
192   list->first = list->last = NULL;
193   return list;
194 }
195 
196 /* destroys a single linked list
197  * args: list
198  */
199 int
s_list_free(s_list * list)200 s_list_free (s_list * list)
201 {
202   list_item *tmp1, *tmp2;
203 
204   tmp1 = list->first;
205   while (tmp1)
206     {
207       tmp2 = tmp1->next;
208       free (tmp1);
209       tmp1 = tmp2;
210     }
211   free (list);
212   return 0;
213 }
214 
215 /* returns the count of items in a list, skips transient and windowlistskip
216  * windows
217  * args: list
218  */
219 int
s_list_count(s_list * list)220 s_list_count (s_list * list)
221 {
222   list_item *tmp;
223   int count = 0;
224 
225   tmp = list->first;
226 
227   while (tmp)
228     {
229       if (!(tmp->flags & WINDOWLISTSKIP))
230 	if (!(tmp->flags & TRANSIENT))
231 	  count++;
232       tmp = tmp->next;
233     }
234   return count;
235 }
236 
237 /* sets up the properties and the supported protocols list */
238 static void
gnome_compliance_init()239 gnome_compliance_init ()
240 {
241   Atom supported_list[12];
242   int count;
243 
244   root_win = RootWindow (dpy, DefaultScreen(dpy));
245 
246   /* supporting WM check */
247   _XA_WIN_SUPPORTING_WM_CHECK = XInternAtom (dpy, "_WIN_SUPPORTING_WM_CHECK",
248 					     False);
249   _XA_WIN_PROTOCOLS = XInternAtom (dpy, "_WIN_PROTOCOLS", False);
250   _XA_WIN_LAYER = XInternAtom (dpy, "_WIN_LAYER", False);
251   _XA_WIN_STATE = XInternAtom (dpy, "_WIN_STATE", False);
252   _XA_WIN_HINTS = XInternAtom (dpy, "_WIN_HINTS", False);
253   _XA_WIN_APP_STATE = XInternAtom (dpy, "_WIN_APP_STATE", False);
254 /*_XA_WIN_EXPANDED_SIZE = XInternAtom(dpy, "_WIN_EXPANDED_SIZE", False);*/
255   _XA_WIN_ICONS = XInternAtom (dpy, "_WIN_ICONS", False);
256   _XA_WIN_WORKSPACE = XInternAtom (dpy, "_WIN_WORKSPACE", False);
257   _XA_WIN_WORKSPACE_COUNT = XInternAtom (dpy, "_WIN_WORKSPACE_COUNT", False);
258   _XA_WIN_WORKSPACE_NAMES = XInternAtom (dpy, "_WIN_WORKSPACE_NAMES", False);
259   _XA_WIN_CLIENT_LIST = XInternAtom (dpy, "_WIN_CLIENT_LIST", False);
260   _XA_WIN_DESKTOP_BUTTON_PROXY = XInternAtom (dpy, "_WIN_DESKTOP_BUTTON_PROXY",
261 					      False);
262 
263   if (PAGES)
264     {
265       _XA_WIN_AREA_COUNT = XInternAtom (dpy, "_WIN_AREA_COUNT", False);
266       _XA_WIN_AREA = XInternAtom (dpy, "_WIN_AREA", False);
267     }
268 
269   /* create the GNOME window */
270   gnome_win = XCreateSimpleWindow (dpy, root_win, 0, 0, 5, 5, 0, 0, 0);
271 
272   /* supported WM check */
273   XChangeProperty (dpy, root_win, _XA_WIN_SUPPORTING_WM_CHECK,
274 	 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &gnome_win, 1);
275   XChangeProperty (dpy, gnome_win, _XA_WIN_SUPPORTING_WM_CHECK,
276 	 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &gnome_win, 1);
277 
278   /* supported protocols */
279   count = 0;
280 
281   supported_list[count++] = _XA_WIN_LAYER;	/* done */
282   supported_list[count++] = _XA_WIN_STATE;	/* done */
283   supported_list[count++] = _XA_WIN_HINTS;	/* done */
284   supported_list[count++] = _XA_WIN_APP_STATE;	/* ???? */
285   /*supported_list[count++] = _XA_WIN_EXPANDED_SIZE; */
286   supported_list[count++] = _XA_WIN_ICONS;	/* ???? */
287   supported_list[count++] = _XA_WIN_WORKSPACE;	/* done */
288   supported_list[count++] = _XA_WIN_WORKSPACE_COUNT;	/* done */
289   supported_list[count++] = _XA_WIN_WORKSPACE_NAMES;	/* done */
290   supported_list[count++] = _XA_WIN_CLIENT_LIST;	/* done */
291   if (PAGES)
292     {
293       supported_list[count++] = _XA_WIN_AREA_COUNT;
294       supported_list[count++] = _XA_WIN_AREA;
295     }
296 
297   XChangeProperty (dpy, root_win, _XA_WIN_PROTOCOLS, XA_ATOM, 32, PropModeReplace,
298 		   (unsigned char *) supported_list, count);
299 
300 }
301 
302 /* sets the number of virtual pages per desk */
303 static void
gnome_set_area_count()304 gnome_set_area_count ()
305 {
306   unsigned long val[2];
307 
308   val[0] = 2;
309   val[1] = 2;
310   XChangeProperty (dpy, root_win, _XA_WIN_AREA_COUNT, XA_CARDINAL, 32,
311 		   PropModeReplace, (unsigned char *) &val, 2);
312 }
313 
314 /* sets the current visible page */
315 static void
gnome_set_current_area(int x,int y)316 gnome_set_current_area (int x, int y)
317 {
318   unsigned long val[2];
319 
320   val[0] = x;
321   val[1] = y;
322   XChangeProperty (dpy, root_win, _XA_WIN_AREA, XA_CARDINAL, 32,
323 		   PropModeReplace, (unsigned char *) &val, 2);
324 }
325 
326 /* sets the current workspace */
327 static void
gnome_set_current_workspace(int current_desk)328 gnome_set_current_workspace (int current_desk)
329 {
330 
331   XChangeProperty (dpy, root_win, _XA_WIN_WORKSPACE, XA_CARDINAL, 32,
332 		   PropModeReplace, (unsigned char *) &current_desk, 1);
333 }
334 
335 /* sets the number of workspaces */
336 static void
gnome_set_workspace_count(int workspaces)337 gnome_set_workspace_count (int workspaces)
338 {
339 
340   XChangeProperty (dpy, root_win, _XA_WIN_WORKSPACE_COUNT, XA_CARDINAL, 32,
341 		   PropModeReplace, (unsigned char *) &workspaces, 1);
342 
343 }
344 
345 /* sets up the 'workspace names' property */
346 static void
gnome_set_workspace_names(int count,char ** names)347 gnome_set_workspace_names (int count, char **names)
348 {
349   XTextProperty text;
350 
351   if (XStringListToTextProperty (names, count, &text))
352     {
353       XSetTextProperty (dpy, root_win, &text, _XA_WIN_WORKSPACE_NAMES);
354       XFree (text.value);
355     }
356 }
357 
358 /* sets the window list property, skips transients and winlistskip windows */
359 static void
gnome_set_client_list(s_list * list)360 gnome_set_client_list (s_list * list)
361 {
362   Window *windows = NULL;
363   int windows_count = 0, count;
364   list_item *tmp;
365 
366   windows_count = s_list_count (list);
367 
368   if (windows_count != 0)
369     {
370       windows = safemalloc (sizeof (Window) * windows_count);
371 
372       if (!windows)
373 	{
374 	  fprintf (stderr, "gnome_set_client_list: malloc failed\n");
375 	  return;
376 	}
377       tmp = list->first;
378       count = 0;
379       while (tmp)
380 	{
381 	  if (!(tmp->flags & WINDOWLISTSKIP))
382 	    {
383 	      if (!(tmp->flags & TRANSIENT))
384 		windows[count++] = tmp->id;
385 	    }
386 
387 	  tmp = tmp->next;
388 	}
389       XChangeProperty (dpy, root_win, _XA_WIN_CLIENT_LIST, XA_CARDINAL, 32,
390 		 PropModeReplace, (unsigned char *) windows, windows_count);
391       free (windows);
392     }
393 }
394 
395 /* translates AS window flags to GNOME state properties */
396 static void
gnome_set_win_hints(list_item * aswin)397 gnome_set_win_hints (list_item * aswin)
398 {
399   long flags = 0;
400 
401   if (aswin->flags & STICKY)
402     flags |= WIN_STATE_STICKY;
403 
404   if (aswin->flags & ICONIFIED)
405     flags |= WIN_STATE_MINIMIZED;
406 
407   if (aswin->flags & SHADED)
408     flags |= WIN_STATE_SHADED;
409 
410   if (!(aswin->flags & STICKY))
411     if (aswin->workspace != current_desk)
412       flags |= WIN_STATE_HID_WORKSPACE;
413 
414   if (aswin->flags & TRANSIENT)
415     flags |= WIN_STATE_HID_TRANSIENT;
416 
417   XChangeProperty (dpy, (Window) aswin->id, _XA_WIN_WORKSPACE, XA_CARDINAL,
418 	       32, PropModeReplace, (unsigned char *) &aswin->workspace, 1);
419 
420   XChangeProperty (dpy, (Window) aswin->id, _XA_WIN_STATE, XA_CARDINAL,
421 		   32, PropModeReplace, (unsigned char *) &flags, 1);
422 }
423 
424 /* layer change requests are handled here, GNOME -> AS */
425 static void
gnome_handle_win_layer(XClientMessageEvent * event,Window id,int layer)426 gnome_handle_win_layer (XClientMessageEvent * event, Window id, int layer)
427 {
428   int new_layer;
429   list_item *aswin;
430   char as_mesg[50];
431 
432 
433   if (event)
434     {
435       new_layer = event->data.l[0];
436       aswin = s_list_find_by_data (window_list, (long) event->window);
437       if (!aswin)
438 	return;
439       id = aswin->id;
440     }
441   else
442     new_layer = layer;
443 
444   /*desktop layers */
445   switch (new_layer)
446     {
447     case WIN_LAYER_DESKTOP:
448       LOG ("    WIN_LAYER_DESKTOP");
449       sprintf (as_mesg, "SetLayer -2\n");
450       SendInfo (fd, as_mesg, id);
451       break;
452     case WIN_LAYER_BELOW:
453       LOG ("    WIN_LAYER_BELOW");
454       sprintf (as_mesg, "SetLayer -1\n");
455       SendInfo (fd, as_mesg, id);
456       break;
457     case WIN_LAYER_NORMAL:
458       LOG ("    WIN_LAYER_NORMAL");
459       sprintf (as_mesg, "SetLayer 0\n");
460       SendInfo (fd, as_mesg, id);
461       break;
462     case WIN_LAYER_ONTOP:
463       LOG ("    WIN_LAYER_ONTOP");
464       sprintf (as_mesg, "SetLayer 1\n");
465       SendInfo (fd, as_mesg, id);
466       break;
467     case WIN_LAYER_DOCK:
468       LOG ("    WIN_LAYER_DOCK");
469       sprintf (as_mesg, "SetLayer 1\n");
470       SendInfo (fd, as_mesg, id);
471       break;
472     case WIN_LAYER_ABOVE_DOCK:
473       LOG ("    WIN_LAYER_ABOVE_DOCK");
474       sprintf (as_mesg, "SetLayer 2\n");
475       SendInfo (fd, as_mesg, id);
476       break;
477     default:
478       fprintf (stderr, "%s: I don't know anything about layer: %d\n", MyName, new_layer);
479       break;
480     }
481 }
482 
483 /* hints change requests are handled here, GNOME -> AS */
484 static void
gnome_handle_win_hints(XClientMessageEvent * event,Window id,long flags)485 gnome_handle_win_hints (XClientMessageEvent * event, Window id, long flags)
486 {
487   list_item *aswin;
488   int hints;
489   Bool update = False;
490   unsigned long tmpflags = 0;
491 
492   if (event)
493     {
494       hints = event->data.l[1];
495       aswin = s_list_find_by_data (window_list, (long) event->window);
496     }
497   else
498     {
499       hints = flags;
500       aswin = s_list_find_by_data (window_list, id);
501     }
502 
503 
504   if (!aswin)
505     {
506       LOG ("gnome_handle_win_hints: got an event for an unknown window");
507       return;
508     }
509 
510   if (hints & WIN_HINTS_SKIP_FOCUS)
511     {
512       tmpflags |= NOFOCUS;
513       update = True;
514       LOG ("    SKIPFOCUS");
515     }
516 
517   if (hints & WIN_HINTS_SKIP_WINLIST)
518     {
519       tmpflags |= WINDOWLISTSKIP;
520       update = True;
521       LOG ("    SKIPWINLIST");
522     }
523   if (hints & WIN_HINTS_DO_NOT_COVER)
524     {
525       tmpflags |= AVOID_COVER;
526       update = True;
527       LOG ("    AVOIDCOVER");
528     }
529   if (hints & WIN_HINTS_SKIP_TASKBAR)
530     {
531       tmpflags |= CIRCULATESKIP;
532       update = True;
533       LOG ("    SKIPTASKBAR");
534     }
535   if (hints & WIN_HINTS_GROUP_TRANSIENT)
536     {
537       tmpflags |= TRANSIENT;
538       update = True;
539       LOG ("    TRANSIENT");
540     }
541   if (hints & WIN_HINTS_FOCUS_ON_CLICK)
542     LOG ("    FOCUSONCLICK ignored");
543 
544   update = True;
545   if (update)
546     {
547       char as_msg[50];
548       sprintf (as_msg, "SET_FLAGS %lu\n", tmpflags);
549       SendInfo (fd, as_msg, aswin->id);
550       gnome_set_client_list (window_list);
551     }
552 }
553 
554 /* state requests, GNOME -> AS */
555 static void
gnome_handle_win_state(XClientMessageEvent * event,Window id,long mask)556 gnome_handle_win_state (XClientMessageEvent * event, Window id, long mask)
557 {
558   long new_members, change_mask;
559   list_item *aswin;
560 
561   if (event)
562     {
563       aswin = s_list_find_by_data (window_list, (long) event->window);
564       change_mask = event->data.l[0];
565       new_members = event->data.l[1];
566     }
567   else
568     {
569       aswin = s_list_find_by_data (window_list, id);
570       new_members = mask;
571       change_mask = 0;
572     }
573   if (!aswin)
574     return;
575 
576   /* stick or unstick */
577   if (new_members & WIN_STATE_STICKY)
578     {
579       if (!(aswin->flags & STICKY))
580 	{
581 	  LOG ("    STICKY req");
582 	  SendInfo (fd, "Stick\n", aswin->id);
583 	}
584     }
585   else if (change_mask & WIN_STATE_STICKY)
586     {
587       if (aswin->flags & STICKY)
588 	{
589 	  LOG ("    UNSTICKY req");
590 	  SendInfo (fd, "Stick\n", aswin->id);
591 	}
592     }
593 
594   if ((new_members & WIN_STATE_MAXIMIZED_VERT) &&
595       (new_members & WIN_STATE_MAXIMIZED_HORIZ))
596     {
597       LOG ("    MAX req");
598       SendInfo (fd, "Maximize 100% 100%\n", aswin->id);
599     }
600   else
601     {
602       /* maximize verticaly */
603       if (new_members & WIN_STATE_MAXIMIZED_VERT)
604 	{
605 	  LOG ("    MAXVERT req");
606 	  SendInfo (fd, "Maximize 0% 100%\n", aswin->id);
607 	}
608 
609       /* maximize horizontaly */
610       if (new_members & WIN_STATE_MAXIMIZED_HORIZ)
611 	{
612 	  LOG ("    MAXHOR req");
613 	  SendInfo (fd, "Maximize 100% 0%\n", aswin->id);
614 	}
615     }
616   /* shade or unshade */
617   if (new_members & WIN_STATE_SHADED)
618     {
619       if (!(aswin->flags & SHADED))
620 	{
621 	  LOG ("    SHADE req");
622 	  SendInfo (fd, "Shade 1\n", aswin->id);
623 	}
624     }
625   else if (change_mask & WIN_STATE_SHADED)
626     {
627       if (aswin->flags & SHADED)
628 	SendInfo (fd, "Shade 0\n", aswin->id);
629     }
630 
631   if (new_members & WIN_STATE_HIDDEN)
632     {
633       LOG ("    HIDDEN req");
634       SendInfo (fd, "Iconify 1\n", aswin->id);
635     }
636   if (new_members & WIN_STATE_MINIMIZED)
637     {
638       LOG ("    MINI req");
639       SendInfo (fd, "Iconify 1\n", aswin->id);
640     }
641   /* check if workspace changed, layers, hints and state are handled
642    * elsewhere
643    */
644   if (event)
645     gnome_get_prop_workspace (aswin->id);
646 }
647 
648 /* initial app state, get GNOME state from windows that set them */
649 static void
gnome_get_prop_state(Window id)650 gnome_get_prop_state (Window id)
651 {
652   Atom ret_type;
653   int fmt;
654   unsigned long nitems, bytes_after;
655   long flags, *data = 0;
656 
657   if (XGetWindowProperty (dpy, id, _XA_WIN_STATE, 0, 1, False,
658 			XA_CARDINAL, &ret_type, &fmt, &nitems, &bytes_after,
659 			  (unsigned char **) &data) == Success && data)
660     {
661       flags = *data;
662       XFree (data);
663       gnome_handle_win_state (NULL, id, flags);
664     }
665 
666 }
667 
668 /* initial app hints, get GNOME hints from apps that use them */
669 static void
gnome_get_prop_hints(Window id)670 gnome_get_prop_hints (Window id)
671 {
672   Atom ret_type;
673   int fmt;
674   unsigned long nitems;
675   unsigned long bytes_after;
676   long flags, *data = 0;
677   if (XGetWindowProperty (dpy, id, _XA_WIN_HINTS, 0, 1, False,
678 			  XA_CARDINAL, &ret_type, &fmt, &nitems,
679 			  &bytes_after,
680 			  (unsigned char **) &data) == Success && data)
681     {
682       flags = *data;
683       XFree (data);
684       gnome_handle_win_hints (NULL, id, flags);
685     }
686 }
687 
688 /* initial app layer, get GNOME layers from apps that use them */
689 static void
gnome_get_prop_layer(Window id)690 gnome_get_prop_layer (Window id)
691 {
692   Atom ret_type;
693   int fmt;
694   unsigned long nitems;
695   unsigned long bytes_after;
696   long val, *data = 0;
697   if (XGetWindowProperty (dpy, id, _XA_WIN_LAYER, 0, 1, False,
698 			  XA_CARDINAL, &ret_type, &fmt, &nitems,
699 			  &bytes_after,
700 			  (unsigned char **) &data) == Success && data)
701     {
702       val = *data;
703       XFree (data);
704       gnome_handle_win_layer (NULL, id, (int) val);
705     }
706 
707 }
708 
709 /* initial app workspace, get it from the window if set */
710 static void
gnome_get_prop_workspace(Window id)711 gnome_get_prop_workspace (Window id)
712 {
713   Atom ret_type;
714   int fmt;
715   unsigned long nitems;
716   unsigned long bytes_after;
717   long val, *data = 0;
718 
719   LOG ("gnome_get_prop_workspace");
720 
721   if (XGetWindowProperty (dpy, id, _XA_WIN_WORKSPACE, 0, 1, False,
722 			  XA_CARDINAL, &ret_type, &fmt, &nitems,
723 			  &bytes_after,
724 			  (unsigned char **) &data) == Success && data)
725     {
726       val = *data;
727       XFree (data);
728       if (val != current_desk)
729 	{
730 	  char msg[50];
731 	  LOG ("ChangeDesk req");
732 	  sprintf (msg, "WindowsDesk %d\n", (int) val);
733 	  SendInfo (fd, msg, id);
734 	}
735 
736     }
737 }
738 
739 /* initial hints, check for GNOME hints on a window */
740 static void
gnome_check_client_hints(Window id)741 gnome_check_client_hints (Window id)
742 {
743   /* state */
744   gnome_get_prop_state (id);
745 
746   /* hints */
747   gnome_get_prop_hints (id);
748 
749   /* layer */
750   gnome_get_prop_layer (id);
751 
752   /* workspace */
753   gnome_get_prop_workspace (id);
754 }
755 
756 /* signal handler, also called by AS when it wants to kill the module */
757 void
DeadPipe(int sig)758 DeadPipe (int sig)
759 {
760 #ifdef DEBUG_ALLOCS
761   int i;
762 #endif
763   switch (sig)
764     {
765     case SIGSEGV:
766       fprintf (stderr, "Segmentation fault in %s, please send a bug report\n", MyName);
767       exit (-1);
768     case SIGINT:
769       fprintf (stderr, "User abort, exiting\n");
770       exit (-1);
771     case SIGPIPE:
772       fprintf (stderr, "pipe\n");
773     default:
774       break;
775     }
776 #ifdef DEBUG_ALLOCS
777   s_list_free (window_list);
778   for (i = 0; i < desk_count; i++)
779     free (desk_names[i]);
780   free (desk_names);
781   print_unfreed_mem ();
782 #endif
783   exit (0);
784 }
785 
786 /* set_as_mask:
787  * set the mask on the pipe
788  */
789 
790 static void
set_as_mask(long unsigned mask)791 set_as_mask (long unsigned mask)
792 {
793   char set_mask_mesg[255];
794 
795   sprintf (set_mask_mesg, "SET_MASK %lu\n", mask);
796   SendInfo (fd, set_mask_mesg, 0);
797 }
798 
799 /* process AS events */
800 static void
process_message(unsigned long type,int elements,unsigned long * body)801 process_message (unsigned long type, int elements, unsigned long *body)
802 {
803   int status = 0;
804 
805   switch (type)
806     {
807     case M_CONFIGURE_WINDOW:
808       status = list_configure (body);
809       break;
810     case M_ADD_WINDOW:
811       status = list_add_window (body);
812       break;
813     case M_DESTROY_WINDOW:
814       status = list_destroy_window (body);
815       break;
816     case M_ICONIFY:
817       status = list_iconify (body);
818       break;
819     case M_DEICONIFY:
820       status = list_deiconify (body);
821       break;
822     case M_WINDOW_NAME:
823       status = list_window_name (body);
824       break;
825     case M_ICON_NAME:
826       status = list_icon_name (body);
827       break;
828     case M_END_WINDOWLIST:
829       status = list_end ();
830       break;
831     case M_NEW_PAGE:
832       if (PAGES)
833 	status = list_page_change (body);
834       break;
835     case M_NEW_DESK:
836       LOG ("M_NEW_DESK");
837       status = list_desk_change (body);
838       break;
839     case M_TOGGLE_PAGING:
840       break;
841     default:
842       break;
843     }
844 }
845 
846 /* list_configure:
847  * pipe configure events
848  */
849 
850 int
list_configure(unsigned long * body)851 list_configure (unsigned long *body)
852 {
853   list_item *item;
854 
855   if (body[0] == gnome_win)
856     return 0;
857 
858   if (body[0] == None)
859     return 0;
860 
861   if ((item = s_list_find_by_data (window_list, body[0])) != NULL)
862     {
863       Bool update = False;
864 
865       if (item->flags != body[8])
866 	{
867 	  item->flags = body[8];
868 	  update = True;
869 	}
870       if (item->workspace != body[7])
871 	{
872 	  if (!(item->flags & ICONIFIED))
873 	    {
874 	      item->workspace = body[7];
875 	      update = True;
876 	    }
877 	}
878 
879       if (update)
880 	{
881 	  gnome_set_win_hints (item);
882 	  gnome_set_client_list (window_list);
883 	  return 1;
884 	}
885       return 0;
886     }
887   return (list_add_window (body));
888 }
889 /* list_add_window:
890  * pipe add window events
891  */
892 
893 int
list_add_window(unsigned long * body)894 list_add_window (unsigned long *body)
895 {
896   list_item *item;
897 
898   LOG ("list_add");
899 
900   if (body[0] == None)
901     return 0;
902 
903   if (s_list_find_by_data (window_list, body[0]))
904     {
905       LOG ("already exists!");
906       return 0;
907     }
908   if (body[0] == gnome_win)
909     return 0;
910 
911   item = s_list_insert (window_list, body[0]);
912   item->flags = body[8];
913   item->workspace = body[7];
914   item->area_x = area_x;
915   item->area_y = area_y;
916   gnome_check_client_hints (item->id);
917   gnome_set_win_hints (item);
918   gnome_set_client_list (window_list);
919   return 1;
920 }
921 
922 /* list_window_name:
923  * pipe chage of window name events
924  */
925 
926 int
list_window_name(unsigned long * body)927 list_window_name (unsigned long *body)
928 {
929   return 0;
930 }
931 
932 /* list_icon_name:
933  * pipe change of icon name events
934  */
935 
936 int
list_icon_name(unsigned long * body)937 list_icon_name (unsigned long *body)
938 {
939   return 0;
940 }
941 
942 /* list_destroy_window:
943  * pipe destroyed windows event
944  */
945 
946 int
list_destroy_window(unsigned long * body)947 list_destroy_window (unsigned long *body)
948 {
949   if (s_list_find_by_data (window_list, body[0]))
950     {
951       s_list_remove_by_data (window_list, body[0]);
952       gnome_set_client_list (window_list);
953       return 1;
954     }
955   return 0;
956 }
957 
958 /* list_end
959  * end of window list from pipe
960  */
961 
962 static int
list_end(void)963 list_end (void)
964 {
965   gnome_set_current_workspace (current_desk);
966   gnome_set_workspace_count (desk_count);
967   gnome_set_workspace_names (desk_count, desk_names);
968   if (PAGES)
969     {
970       gnome_set_area_count ();
971       gnome_set_current_area (0, 0);
972     }
973   gnome_set_client_list (window_list);
974   return 0;
975 }
976 
977 /* list_deiconify:
978  * window deiconified from the pipe
979  */
980 
981 int
list_deiconify(unsigned long * body)982 list_deiconify (unsigned long *body)
983 {
984   return 0;
985 }
986 
987 /* list_iconify:
988  * window iconified from the pipe
989  */
990 
991 int
list_iconify(unsigned long * body)992 list_iconify (unsigned long *body)
993 {
994   return 0;
995 }
996 
997 /*rafa */
998 int
list_page_change(unsigned long * body)999 list_page_change (unsigned long *body)
1000 {
1001   current_desk = (int) body[2];
1002 
1003   if (body[2] != 10000)
1004     {
1005 
1006       if (body[0] > 0)
1007 	area_x = 1;
1008       else
1009 	area_x = 0;
1010 
1011       if (body[1] > 0)
1012 	area_y = 1;
1013       else
1014 	area_y = 0;
1015       gnome_set_current_area (area_x, area_y);
1016       XFlush (dpy);
1017     }
1018   return 0;
1019 }
1020 
1021 int
list_desk_change(unsigned long * body)1022 list_desk_change (unsigned long *body)
1023 {
1024   if (body[0] != 10000)
1025     {
1026       current_desk = (unsigned int) body[0];
1027       gnome_set_current_workspace (current_desk);
1028     }
1029   return 0;
1030 }
1031 static int
error_handler(Display * disp,XErrorEvent * event)1032 error_handler (Display * disp, XErrorEvent * event)
1033 {
1034 /*    fprintf (stderr, "%s: internal error, error code %d, request code %d, minor code %d\n  . Please send a bug report\n", MyName, event->error_code, event->request_code, event->minor_code); */
1035   return 0;
1036 }
1037 
1038 static void
event_handler()1039 event_handler ()
1040 {
1041   XEvent Event;
1042   char tmp[255];
1043 
1044   while (XPending (dpy))
1045     {
1046       XNextEvent (dpy, &Event);
1047       switch (Event.type)
1048 	{
1049 	case PropertyNotify:
1050 	  if (Event.xproperty.window == gnome_win)
1051 	    {
1052 	      gnome_check_client_hints (Event.xclient.window);
1053 	      LOG ("PropNot");
1054 	    }
1055 	  break;
1056 	case ClientMessage:
1057 	  /* change workspace request */
1058 	  if (Event.xclient.message_type == _XA_WIN_WORKSPACE)
1059 	    {
1060 	      int desk;
1061 	      desk = Event.xclient.data.l[0];
1062 	      sprintf (tmp, "Desk 10000 %d\n", desk);
1063 	      SendInfo (fd, tmp, None);
1064 	    }
1065 	  /* change layer request */
1066 	  else if (Event.xclient.message_type == _XA_WIN_LAYER)
1067 	    {
1068 	      LOG ("_XA_WIN_LAYER request");
1069 	      gnome_handle_win_layer (&Event.xclient, None, 0);
1070 	    }
1071 	  /* state requests */
1072 	  else if (Event.xclient.message_type == _XA_WIN_STATE)
1073 	    {
1074 	      LOG ("_XA_WIN_STATE request");
1075 	      gnome_handle_win_state (&Event.xclient, None, 0);
1076 	    }
1077 	  /* win hints requests */
1078 	  else if (Event.xclient.message_type == _XA_WIN_HINTS)
1079 	    {
1080 	      LOG ("_XA_WIN_HINTS request");
1081 	      gnome_handle_win_hints (&Event.xclient, None, 0);
1082 	    }
1083 	  break;
1084 	default:
1085 	  break;
1086 	}
1087     }
1088 }
1089 /******************************************************************************
1090   EndLessLoop -  Read and redraw until we get killed, blocking when can't read
1091 ******************************************************************************/
1092 static void
EndLessLoop()1093 EndLessLoop ()
1094 {
1095   fd_set readset;
1096   struct timeval tv;
1097 
1098   while (1)
1099     {
1100       FD_ZERO (&readset);
1101       FD_SET (fd[1], &readset);
1102       FD_SET (x_fd, &readset);
1103       tv.tv_sec = 0;
1104       tv.tv_usec = 0;
1105 #ifdef __hpux
1106       if (!select (fd_width, (int *) &readset, NULL, NULL, &tv))
1107 	{
1108 	  XPending (dpy);
1109 	  FD_ZERO (&readset);
1110 	  FD_SET (fd[1], &readset);
1111 	  FD_SET (x_fd, &readset);
1112 	  select (fd_width, (int *) &readset, NULL, NULL, NULL);
1113 	}
1114 #else
1115       if (!select (fd_width, &readset, NULL, NULL, &tv))
1116 	{
1117 	  XPending (dpy);
1118 	  FD_ZERO (&readset);
1119 	  FD_SET (fd[1], &readset);
1120 	  FD_SET (x_fd, &readset);
1121 	  select (fd_width, &readset, NULL, NULL, NULL);
1122 	}
1123 #endif
1124       if (FD_ISSET (x_fd, &readset))
1125 	event_handler ();
1126       if (!FD_ISSET (fd[1], &readset))
1127 	continue;
1128       read_as_socket ();
1129     }
1130 }
1131 
1132 static void
read_as_socket()1133 read_as_socket ()
1134 {
1135   unsigned long header[3], *body;
1136   if (ReadASPacket (fd[1], header, &body) > 0)
1137     {
1138       process_message (header[1], header[2], body);
1139       free (body);
1140     }
1141 }
1142 
1143 static void
version(void)1144 version (void)
1145 {
1146   printf ("%s version %s\n%s\n", MyName, VERSION, cvsident);
1147   exit (0);
1148 }
1149 
1150 static void
usage(void)1151 usage (void)
1152 {
1153   printf ("Usage:\n"
1154 	  "%s [-f [config file]] [-v|--version] [-h|--help]\n", MyName);
1155   exit (0);
1156 }
1157 
1158 static void
default_config()1159 default_config ()
1160 {
1161   int i;
1162 
1163   desk_count = 4;
1164 
1165   desk_names = safemalloc (sizeof (char *));
1166 
1167   for (i = 0; i < 4; i++)
1168     {
1169       if (i != 0)
1170 	desk_names = realloc (desk_names, sizeof (char *) + i * sizeof (char *));
1171       desk_names[i] = safemalloc (10);
1172       sprintf (desk_names[i], "Desktop %i", i);
1173     }
1174 }
1175 
1176 
1177 static void
parse_config(char * cfgfile)1178 parse_config (char *cfgfile)
1179 {
1180   char *line, *tline, *tmp;
1181   FILE *ptr;
1182   int len;
1183 
1184   if ((ptr = fopen (cfgfile, "r")) == NULL)
1185     {
1186       fprintf (stderr, "%s, can\'t open config file %s, using defaults\n", MyName, "file");
1187       default_config ();
1188       return;
1189     }
1190 
1191   line = (char *) safemalloc (MAXLINELENGTH);
1192   len = strlen (MyName);
1193 
1194 
1195   while ((tline = fgets (line, MAXLINELENGTH, ptr)) != NULL)
1196     {
1197       while (isspace (*tline))
1198 	tline++;
1199       if ((*tline == '*') && (!mystrncasecmp (tline + 1, MyName, len)))
1200 	{
1201 	  tline += len + 1;
1202 	  if (!mystrncasecmp (tline, "DeskName", 8))
1203 	    {
1204 	      if (desk_count == 0)
1205 		desk_names = malloc (sizeof (char *));
1206 	      else
1207 		desk_names = realloc (desk_names,
1208 			    sizeof (char *) + desk_count * sizeof (char *));
1209 	      tmp = tline + 8 + 1;
1210 	      desk_names[desk_count] = (char *) malloc (strlen (tmp) + 1);
1211 	      strcpy (desk_names[desk_count], tmp);
1212 	      desk_count++;
1213 	    }
1214 	  if (!mystrncasecmp (tline, "Pages", 5))
1215 	    {
1216 	      if (atoi (tline + 5) > 0)
1217 		{
1218 		  LOG ("PAGING IS ON");
1219 		  PAGES = True;
1220 		}
1221 	    }
1222 	}
1223     }
1224 
1225   if (desk_count == 0)
1226     default_config ();
1227 
1228   free (line);
1229   fclose (ptr);
1230 }
1231 
1232 int
main(int argc,char ** argv)1233 main (int argc, char **argv)
1234 {
1235   char *temp;
1236   int i;
1237   char *global_config_file = NULL;
1238   char tmp[128];
1239   FILE *fp = NULL;
1240 
1241   /* Save our program name - for error messages */
1242   temp = strrchr (argv[0], '/');
1243   MyName = temp ? temp + 1 : argv[0];
1244 
1245   for (i = 1; i < argc && *argv[i] == '-'; i++)
1246     {
1247       if (!strcmp (argv[i], "-h") || !strcmp (argv[i], "--help"))
1248 	usage ();
1249       else if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "--version"))
1250 	version ();
1251       else if (!strcmp (argv[i], "-w") || !strcmp (argv[i], "--window"))
1252 	i++;
1253       else if (!strcmp (argv[i], "-c") || !strcmp (argv[i], "--context"))
1254 	i++;
1255       else if (!strcmp (argv[i], "-f") && i + 1 < argc)
1256 	global_config_file = argv[++i];
1257     }
1258 
1259   /* Dead pipe == dead AfterStep */
1260   signal (SIGPIPE, DeadPipe);
1261   signal (SIGSEGV, DeadPipe);
1262   signal (SIGINT, DeadPipe);
1263 
1264   if ((dpy = XOpenDisplay ("")) == NULL)
1265     {
1266       fprintf (stderr, "%s: couldn't open display %s\n",
1267 	       MyName, XDisplayName (""));
1268       exit (1);
1269     }
1270   set_current_X_display (dpy);
1271   screen = DefaultScreen (dpy);
1272 
1273   /* connect to AfterStep */
1274   temp = module_get_socket_property (RootWindow (dpy, screen));
1275   fd[0] = fd[1] = module_connect (temp);
1276   XFree (temp);
1277   if (fd[0] < 0)
1278     {
1279       fprintf (stderr, "%s: unable to establish connection to AfterStep\n", MyName);
1280       exit (1);
1281     }
1282   temp = safemalloc (9 + strlen (MyName) + 1);
1283   sprintf (temp, "SET_NAME %s", MyName);
1284   SendInfo (fd, temp, None);
1285   free (temp);
1286 
1287   x_fd = XConnectionNumber (dpy);
1288   fd_width = GetFdWidth ();
1289 
1290   XSetErrorHandler (error_handler);
1291 
1292   if (global_config_file != NULL)
1293     temp = PutHome (global_config_file);
1294   else
1295     {
1296       memset (tmp, 128, '\0');
1297       sprintf (tmp, "%s/Gnome", AFTER_DIR);
1298       temp = PutHome (tmp);
1299       if ((fp = fopen (temp, "r")) == NULL)
1300 	{
1301 	  sprintf (tmp, "%s/Gnome", AFTER_SHAREDIR);
1302 	  free (temp);
1303 	  temp = PutHome (tmp);
1304 	}
1305     }
1306 
1307   if (fp)
1308     fclose (fp);
1309 
1310   parse_config (temp);
1311   free (temp);
1312 
1313   gnome_compliance_init ();
1314 
1315   window_list = s_list_new ();
1316 
1317 
1318   set_as_mask ((long unsigned) mask_reg);
1319   SendInfo (fd, "Send_WindowList\n", 0);
1320 
1321   XSelectInput (dpy, root_win, PropertyChangeMask | SubstructureNotifyMask);
1322   XSelectInput (dpy, gnome_win, PropertyChangeMask);
1323 
1324   EndLessLoop ();
1325   return 0;
1326 }
1327