1 /*
2 * Copyright (c) 2000 Sasha Vasko <sashav@sprintmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 */
19
20 #define LOCAL_DEBUG
21
22 #include "../../configure.h"
23
24 #if TIME_WITH_SYS_TIME
25 # include <sys/time.h>
26 # include <time.h>
27 #else
28 # if HAVE_SYS_TIME_H
29 # include <sys/time.h>
30 # else
31 # include <time.h>
32 # endif
33 #endif
34
35 #include "asinternals.h"
36 #include "../../libAfterStep/session.h"
37 #include "../../libAfterStep/wmprops.h"
38
39 Bool on_dead_aswindow (Window w);
40 /********************************************************************************/
41 /* window list management */
42
auto_destroy_aswindow(void * data)43 void auto_destroy_aswindow (void *data)
44 {
45 if (data && ((ASMagic *) data)->magic == MAGIC_ASWINDOW) {
46 ASWindow *asw = (ASWindow *) data;
47 Destroy (asw, False);
48 }
49 }
50
51 void destroy_aslayer (ASHashableValue value, void *data);
52 void destroy_window_group (ASHashableValue value, void *data);
53
init_aswindow_list()54 ASWindowList *init_aswindow_list ()
55 {
56 ASWindowList *list;
57
58 list = safecalloc (1, sizeof (ASWindowList));
59 list->clients = create_asbidirlist (auto_destroy_aswindow);
60 list->aswindow_xref = create_ashash (0, NULL, NULL, NULL);
61 list->layers =
62 create_ashash (7, NULL, desc_long_compare_func, destroy_aslayer);
63 list->bookmarks =
64 create_ashash (7, string_hash_value, string_compare,
65 string_destroy_without_data);
66
67 list->window_groups =
68 create_ashash (0, NULL, NULL, destroy_window_group);
69
70 list->circulate_list = create_asvector (sizeof (ASWindow *));
71 list->sticky_list = create_asvector (sizeof (ASWindow *));
72
73 list->stacking_order = create_asvector (sizeof (ASWindow *));
74
75 Scr.on_dead_window = on_dead_aswindow;
76
77 return list;
78 }
79
80 struct SaveWindowAuxData {
81 char this_host[MAXHOSTNAME];
82 FILE *f;
83 ASHashTable *res_name_counts;
84 Bool only_modules;
85 ASWindowGroup *group;
86 };
87
get_res_name_count(ASHashTable * res_name_counts,char * res_name)88 int get_res_name_count (ASHashTable * res_name_counts, char *res_name)
89 {
90 ASHashData hdata;
91
92 if (res_name == NULL || res_name_counts == NULL)
93 return 0;
94 if (get_hash_item (res_name_counts, AS_HASHABLE (res_name), &hdata.vptr)
95 == ASH_Success) {
96 int val = *(hdata.iptr);
97 ++val;
98 *(hdata.iptr) = val;
99 return val;
100 } else {
101 hdata.iptr = safemalloc (sizeof (int));
102 *(hdata.iptr) = 1;
103 add_hash_item (res_name_counts, AS_HASHABLE (mystrdup (res_name)),
104 hdata.vptr);
105 }
106 return 1;
107 }
108
check_aswindow_name_unique(char * name,ASWindow * asw)109 Bool check_aswindow_name_unique (char *name, ASWindow * asw)
110 {
111 ASBiDirElem *e = LIST_START (Scr.Windows->clients);
112 while (e != NULL) {
113 ASWindow *curr = (ASWindow *) LISTELEM_DATA (e);
114 if (curr != asw && strcmp (ASWIN_NAME (curr), name) == 0)
115 return False;
116 LIST_GOTO_NEXT (e);
117 }
118 return True;
119 }
120
121 // ask for program command line
pid2cmd(int pid)122 char *pid2cmd (int pid)
123 {
124 #define MAX_CMDLINE_SIZE 2048
125 FILE *f;
126 static char buf[MAX_CMDLINE_SIZE];
127 static char path[128];
128 char *cmd = NULL;
129
130 sprintf (path, "/proc/%d/cmdline", pid);
131 if ((f = fopen (path, "r")) != NULL) {
132 if (fgets (buf, MAX_CMDLINE_SIZE, f) != NULL) {
133 buf[MAX_CMDLINE_SIZE - 1] = '\0';
134 cmd = mystrdup (&buf[0]);
135 }
136 fclose (f);
137 }
138 return cmd;
139 }
140
141 static
filter_out_geometry(char * cmd_args,char ** geom_keyword,char ** original_size)142 char *filter_out_geometry (char *cmd_args, char **geom_keyword,
143 char **original_size)
144 {
145 char *clean = mystrdup (cmd_args);
146 char *token_start;
147 char *token_end = clean;
148
149 do {
150 token_start = token_end;
151
152 if (token_start[0] == '-') {
153 if (strncmp (token_start, "-g ", 3) == 0 ||
154 strncmp (token_start, "-geometry ", 10) == 0 ||
155 strncmp (token_start, "--geometry ", 11) == 0) {
156 unsigned int width = 0;
157 unsigned int height = 0;
158 int flags = 0;
159 char *geom_start = token_start;
160 if (geom_keyword)
161 *geom_keyword = tokencpy (geom_start);
162 token_end = tokenskip (token_start, 1);
163 if (token_end > token_start) {
164 token_start = token_end;
165 token_end =
166 parse_geometry (token_start, NULL, NULL, &width, &height,
167 &flags);
168 if (token_end > token_start) {
169 int i = 0;
170 for (i = 0; token_end[i] != '\0'; ++i)
171 geom_start[i] = token_end[i];
172 geom_start[i] = '\0';
173 }
174 }
175 if (original_size && get_flags (flags, WidthValue | HeightValue)) {
176 char *tmp = *original_size = safemalloc (64);
177 if (get_flags (flags, WidthValue) && width > 0) {
178 sprintf (tmp, "%d", width);
179 while (*tmp)
180 ++tmp;
181 }
182 if (get_flags (flags, HeightValue) && height > 0)
183 sprintf (tmp, "x%d", height);
184 }
185 break;
186 }
187 }
188 token_end = tokenskip (token_start, 1);
189 } while (token_end > token_start);
190 return clean;
191 }
192
stripreplace_geometry_size(char ** pgeom,char * original_size)193 static void stripreplace_geometry_size (char **pgeom, char *original_size)
194 {
195 char *geom = *pgeom;
196 if (geom) {
197
198 int i = 0;
199 if (isdigit (geom[0]))
200 while (geom[++i])
201 if (geom[i] == '+' || geom[i] == '-')
202 break;
203 if (original_size != NULL || i > 0) {
204 int size = strlen (&geom[i]);
205 if (original_size)
206 size += strlen (original_size);
207 *pgeom = safemalloc (size + 1);
208 if (original_size)
209 sprintf (*pgeom, "%s%s", original_size, &geom[i]);
210 else
211 strcpy (*pgeom, &geom[i]);
212 free (geom);
213 }
214 }
215 }
216
make_application_name(ASWindow * asw,char * rclass,char * rname)217 static char *make_application_name (ASWindow * asw, char *rclass,
218 char *rname)
219 {
220 char *name = ASWIN_NAME (asw);
221 /* need to check if we can use window name in the pattern. It has to be :
222 * 1) Not changed since window initial mapping
223 * 2) all ASCII
224 * 3) shorter then 80 chars
225 * 4) must not match class or res_name
226 * 5) Unique
227 */
228 if (name == rname || name == rclass)
229 name = NULL;
230 else if (name != NULL && get_flags (asw->internal_flags, ASWF_NameChanged)) { /* allow changed names only for terms, as those could launch sub app inside */
231 int rclass_len = rclass ? strlen (rclass) : 0;
232 if (rclass_len != 5)
233 name = NULL;
234 else if (strstr (rclass + 1, "term") != 0)
235 name = NULL;
236 }
237 if (name) {
238 int i = 0;
239 while (name[i] != '\0') { /* we do not want to have path in names as well */
240 if (!isalnum (name[i]) && !isspace (name[i]))
241 break;
242 if (++i >= 80)
243 break;
244 }
245 if (name[i] != '\0')
246 name = NULL;
247 else {
248 if (strcmp (rclass, name) == 0 || strcmp (rname, name) == 0)
249 name = NULL;
250 else /* check that its unique */
251 if (!check_aswindow_name_unique (name, asw))
252 name = NULL;
253 }
254 }
255 return name;
256 }
257
do_save_aswindow(ASWindow * asw,struct SaveWindowAuxData * swad)258 static void do_save_aswindow (ASWindow * asw,
259 struct SaveWindowAuxData *swad)
260 {
261 Bool same_host = (asw->hints->client_host == NULL
262 || mystrcasecmp (asw->hints->client_host, swad->this_host) == 0);
263 if (asw->hints->client_cmd == NULL && same_host) {
264 if (ASWIN_HFLAGS (asw, AS_PID) && asw->hints->pid > 0)
265 asw->hints->client_cmd = pid2cmd (asw->hints->pid);
266
267 }
268 LOCAL_DEBUG_OUT ("same_host = %d, client_smd = \"%s\"", same_host,
269 asw->hints->client_cmd ? asw->hints->
270 client_cmd : "(null)");
271
272 if (asw->hints->client_cmd != NULL && same_host) {
273 char *pure_geometry = NULL;
274 char *geom =
275 make_client_geometry_string (ASDefaultScr, asw->hints, asw->status,
276 &(asw->anchor), Scr.Vx, Scr.Vy,
277 &pure_geometry);
278 /* format : [<res_class>]:[<res_name>]:[[#<seq_no>]|<name>] */
279 int app_no =
280 get_res_name_count (swad->res_name_counts, asw->hints->res_name);
281 char *rname = asw->hints->res_name ? asw->hints->res_name : "*";
282 char *rclass = asw->hints->res_class ? asw->hints->res_class : "*";
283 char *name = ASWIN_NAME (asw);
284 char *app_name = "*";
285 char *cmd_app = NULL, *cmd_args;
286 Bool supports_geometry = False;
287 char *geometry_keyword = NULL;
288 char *clean_cmd_args = NULL;
289 char *original_size = NULL;
290
291 name = make_application_name (asw, rclass, rname);
292
293 if (name == NULL) {
294 app_name =
295 safemalloc (strlen (rclass) + 1 + strlen (rname) + 1 + 1 + 15 +
296 1);
297 sprintf (app_name, "%s:%s:#%d", rclass, rname, app_no);
298 } else {
299 app_name =
300 safemalloc (strlen (rclass) + 1 + strlen (rname) + 1 +
301 strlen (name) + 1);
302 sprintf (app_name, "%s:%s:%s", rclass, rname, name);
303 }
304
305 cmd_args = parse_token (asw->hints->client_cmd, &cmd_app);
306 if (cmd_app != NULL) /* we want -geometry to be the first arg, so that terms could correctly launch app with -e arg */
307 clean_cmd_args =
308 filter_out_geometry (cmd_args, &geometry_keyword,
309 &original_size);
310
311 if (geometry_keyword == NULL && ASWIN_HFLAGS (asw, AS_Module))
312 geometry_keyword = mystrdup ("--geometry");
313
314 if (geometry_keyword == NULL) {
315 #if 0 /* this does more bad then good */
316 geometry_keyword = mystrdup ("-geometry");
317 #endif
318 } else
319 supports_geometry = True;
320
321 if (!ASWIN_HFLAGS (asw, AS_Handles)) { /* we want to remove size from geometry here,
322 * unless it was requested in original cmd-line geometry */
323 stripreplace_geometry_size (&geom, original_size);
324 }
325
326 fprintf (swad->f, "\tExec \"I:%s\" %s", app_name,
327 cmd_app ? cmd_app : asw->hints->client_cmd);
328 if (geometry_keyword)
329 fprintf (swad->f, " %s %s", geometry_keyword, geom);
330 #if 0
331 if (asw->group && asw->group->sm_client_id)
332 fprintf (swad->f, " --sm-client-id %s", asw->group->sm_client_id);
333 #endif
334 if (cmd_app != NULL) {
335 fprintf (swad->f, " %s", clean_cmd_args);
336 destroy_string (&cmd_app);
337 }
338 fprintf (swad->f, " &\n");
339
340 destroy_string (&clean_cmd_args);
341 destroy_string (&geometry_keyword);
342 destroy_string (&original_size);
343
344 if (ASWIN_HFLAGS (asw, AS_Module)) {
345 fprintf (swad->f, "\tWait \"I:%s\" Layer %d\n", app_name,
346 ASWIN_LAYER (asw));
347 } else if (ASWIN_GET_FLAGS (asw, AS_Sticky)) {
348 if (supports_geometry)
349 fprintf (swad->f, "\tWait \"I:%s\" Layer %d"
350 ", Sticky"
351 ", StartsOnDesk %d"
352 ", %s"
353 "\n",
354 app_name, ASWIN_LAYER (asw),
355 ASWIN_DESK (asw),
356 ASWIN_GET_FLAGS (asw,
357 AS_Iconic) ? "StartIconic" :
358 "StartNormal");
359 else
360 fprintf (swad->f, "\tWait \"I:%s\" DefaultGeometry %s"
361 ", Layer %d"
362 ", Sticky"
363 ", StartsOnDesk %d"
364 ", %s"
365 "\n",
366 app_name, pure_geometry,
367 ASWIN_LAYER (asw),
368 ASWIN_DESK (asw),
369 ASWIN_GET_FLAGS (asw,
370 AS_Iconic) ? "StartIconic" :
371 "StartNormal");
372 } else {
373 if (supports_geometry)
374 fprintf (swad->f, "\tWait \"I:%s\" Layer %d"
375 ", Slippery"
376 ", StartsOnDesk %d"
377 ", ViewportX %d"
378 ", ViewportY %d"
379 ", %s"
380 "\n",
381 app_name, ASWIN_LAYER (asw),
382 ASWIN_DESK (asw),
383 ((asw->status->x +
384 asw->status->viewport_x) / Scr.MyDisplayWidth) *
385 Scr.MyDisplayWidth,
386 ((asw->status->y +
387 asw->status->viewport_y) / Scr.MyDisplayHeight) *
388 Scr.MyDisplayHeight, ASWIN_GET_FLAGS (asw,
389 AS_Iconic) ?
390 "StartIconic" : "StartNormal");
391 else
392 fprintf (swad->f, "\tWait \"I:%s\" DefaultGeometry %s"
393 ", Layer %d"
394 ", Slippery"
395 ", StartsOnDesk %d"
396 ", ViewportX %d"
397 ", ViewportY %d"
398 ", %s"
399 "\n",
400 app_name, pure_geometry,
401 ASWIN_LAYER (asw),
402 ASWIN_DESK (asw),
403 ((asw->status->x +
404 asw->status->viewport_x) / Scr.MyDisplayWidth) *
405 Scr.MyDisplayWidth,
406 ((asw->status->y +
407 asw->status->viewport_y) / Scr.MyDisplayHeight) *
408 Scr.MyDisplayHeight, ASWIN_GET_FLAGS (asw,
409 AS_Iconic) ?
410 "StartIconic" : "StartNormal");
411 }
412 destroy_string (&pure_geometry);
413 destroy_string (&geom);
414 destroy_string (&app_name);
415 }
416 }
417
make_aswindow_cmd_iter_func(void * data,void * aux_data)418 Bool make_aswindow_cmd_iter_func (void *data, void *aux_data)
419 {
420 struct SaveWindowAuxData *swad = (struct SaveWindowAuxData *)aux_data;
421 ASWindow *asw = (ASWindow *) data;
422 LOCAL_DEBUG_OUT ("window \"%s\", is a %smodule", ASWIN_NAME (asw),
423 ASWIN_HFLAGS (asw, AS_Module) ? " " : "non ");
424 if (asw && swad) {
425
426 /* don't want to save windows with short life span - wharf subfolders, menus, dialogs, etc. */
427 LOCAL_DEBUG_OUT ("short lived? %s", ASWIN_HFLAGS (asw, AS_ShortLived) ? "yes" : "no");
428 if (ASWIN_HFLAGS (asw, AS_ShortLived))
429 return True;
430
431 /* don't want to save modules - those are started from autoexec anyways */
432 if (ASWIN_HFLAGS (asw, AS_Module)) {
433 if (!swad->only_modules)
434 return True;
435 } else if (swad->only_modules)
436 return True;
437 LOCAL_DEBUG_OUT ("group = %p", asw->group);
438 /* we only restart the group leader, then let it worry about its children */
439 if (asw->group != swad->group)
440 return True;
441
442 do_save_aswindow (asw, swad);
443 return (swad->group == NULL);
444 }
445 return False;
446 }
447
save_aswindow_list(ASWindowList * list,char * file,Bool skip_session_managed)448 void save_aswindow_list (ASWindowList * list, char *file, Bool skip_session_managed)
449 {
450 struct SaveWindowAuxData swad;
451
452 if (list == NULL)
453 return;
454
455 if (!mygethostname (swad.this_host, MAXHOSTNAME)) {
456 show_error ("Could not get HOST environment variable!");
457 return;
458 }
459
460 if (file != NULL) {
461 char *realfilename = PutHome (file);
462 if (realfilename == NULL)
463 return;
464
465 swad.f = fopen (realfilename, "w+");
466 if (swad.f == NULL)
467 show_error
468 ("Unable to save your workspace state into the %s - cannot open file for writing!",
469 realfilename);
470 free (realfilename);
471 } else {
472 swad.f = fopen (get_session_ws_file (Session, False), "w+");
473 if (swad.f == NULL)
474 show_error
475 ("Unable to save your workspace state into the default file %s - cannot open file for writing!",
476 get_session_ws_file (Session, False));
477 }
478
479 if (swad.f) {
480 ASHashIterator it;
481 fprintf (swad.f, "Function \"WorkspaceState\"\n");
482 swad.group = NULL;
483 swad.only_modules = False;
484 swad.res_name_counts = create_ashash (0, string_hash_value, string_compare, string_destroy);
485 iterate_asbidirlist (list->clients, make_aswindow_cmd_iter_func, &swad, NULL, False);
486
487 if (start_hash_iteration (list->window_groups, &it)) {
488 do {
489 ASWindowGroup *g = (ASWindowGroup *) curr_hash_data (&it);
490 if (g) {
491 LOCAL_DEBUG_OUT ("group \"%lx\", sm-client-id = \"%s\"",
492 (unsigned long)g->leader,
493 g->sm_client_id ? g->sm_client_id : "none");
494 if (!skip_session_managed || !g->sm_client_id) {
495 swad.group = g;
496 iterate_asbidirlist (list->clients, make_aswindow_cmd_iter_func,
497 &swad, NULL, False);
498 }
499 }
500 } while (next_hash_item (&it));
501 swad.group = NULL;
502 }
503
504 destroy_ashash (&(swad.res_name_counts));
505 fprintf (swad.f, "EndFunction\n\n");
506
507 fprintf (swad.f, "Function \"WorkspaceModules\"\n");
508 swad.only_modules = True;
509 swad.res_name_counts =
510 create_ashash (0, string_hash_value, string_compare,
511 string_destroy);
512 iterate_asbidirlist (list->clients, make_aswindow_cmd_iter_func, &swad,
513 NULL, False);
514 destroy_ashash (&(swad.res_name_counts));
515 fprintf (swad.f, "EndFunction\n");
516 fclose (swad.f);
517 }
518 }
519
520 typedef struct ASCloseWindowAuxData {
521 Bool skipSessionManaged;
522 }ASCloseWindowAuxData;
523
close_aswindow_iter_func(void * data,void * aux_data)524 Bool close_aswindow_iter_func (void *data, void *aux_data)
525 {
526 ASWindow *asw = (ASWindow *) data;
527 struct ASCloseWindowAuxData *auxd = (struct ASCloseWindowAuxData*)aux_data;
528 if (asw == NULL)
529 return False;
530
531 if (!ASWIN_HFLAGS (asw, AS_Module)
532 && get_flags (asw->hints->protocols, AS_DoesWmDeleteWindow)) {
533 if (auxd->skipSessionManaged && asw->group && asw->group->sm_client_id)
534 return True;
535 LOCAL_DEBUG_OUT ("sending delete window request to 0x%lX", (unsigned long)asw->w);
536 send_wm_protocol_request (asw->w, _XA_WM_DELETE_WINDOW, CurrentTime);
537 }
538 return True;
539 }
540
close_aswindow_list(ASWindowList * list,Bool skip_session_managed)541 void close_aswindow_list (ASWindowList * list, Bool skip_session_managed)
542 {
543 ASCloseWindowAuxData aux;
544 aux.skipSessionManaged = skip_session_managed;
545 iterate_asbidirlist (list->clients, close_aswindow_iter_func, &aux, NULL, False);
546 }
547
548
destroy_aswindow_list(ASWindowList ** list,Bool restore_root)549 void destroy_aswindow_list (ASWindowList ** list, Bool restore_root)
550 {
551 if (list)
552 if (*list) {
553 if (restore_root)
554 InstallRootColormap ();
555
556 destroy_asbidirlist (&((*list)->clients));
557 destroy_ashash (&((*list)->aswindow_xref));
558 destroy_ashash (&((*list)->layers));
559 destroy_ashash (&((*list)->bookmarks));
560 destroy_asvector (&((*list)->sticky_list));
561 destroy_asvector (&((*list)->circulate_list));
562 destroy_asvector (&((*list)->stacking_order));
563
564 free (*list);
565 *list = NULL;
566 }
567 }
568
569 /*************************************************************************
570 * We maintain crossreference of X Window ID to ASWindow structure - that is
571 * faster then using XContext since we don't have to worry about multiprocessing,
572 * thus saving time on interprocess synchronization, that Xlib has to do in
573 * order to access list of window contexts.
574 *************************************************************************/
window2ASWindow(Window w)575 ASWindow *window2ASWindow (Window w)
576 {
577 ASHashData hdata = { 0 };
578 if (Scr.Windows->aswindow_xref)
579 if (get_hash_item
580 (Scr.Windows->aswindow_xref, AS_HASHABLE (w),
581 &hdata.vptr) != ASH_Success)
582 hdata.vptr = NULL;
583 return hdata.vptr;
584 }
585
window2ASWindowGroup(Window w)586 ASWindowGroup *window2ASWindowGroup (Window w)
587 {
588 ASHashData hdata = { 0 };
589 if (Scr.Windows->window_groups)
590 if (get_hash_item
591 (Scr.Windows->window_groups, AS_HASHABLE (w),
592 &hdata.vptr) != ASH_Success)
593 hdata.vptr = NULL;
594 return hdata.vptr;
595 }
596
register_aswindow(Window w,ASWindow * asw)597 Bool register_aswindow (Window w, ASWindow * asw)
598 {
599 if (w && asw)
600 return (add_hash_item
601 (Scr.Windows->aswindow_xref, AS_HASHABLE (w),
602 asw) == ASH_Success);
603 return False;
604 }
605
unregister_aswindow(Window w)606 Bool unregister_aswindow (Window w)
607 {
608 if (w)
609 return (remove_hash_item
610 (Scr.Windows->aswindow_xref, AS_HASHABLE (w), NULL,
611 False) == ASH_Success);
612 return False;
613 }
614
destroy_registered_window(Window w)615 Bool destroy_registered_window (Window w)
616 {
617 Bool res = False;
618 if (w) {
619 res = unregister_aswindow (w);
620 XDestroyWindow (dpy, w);
621 }
622 return res;
623 }
624
bookmark_aswindow(ASWindow * asw,char * bookmark)625 Bool bookmark_aswindow (ASWindow * asw, char *bookmark)
626 {
627 Bool success = False;
628 if (bookmark) {
629 remove_hash_item (Scr.Windows->bookmarks, AS_HASHABLE (bookmark), NULL,
630 False);
631 LOCAL_DEBUG_OUT ("Bookmark \"%s\" cleared", bookmark);
632 if (asw) {
633 ASHashData hd;
634 char *tmp = mystrdup (bookmark);
635 hd.c32 = asw->w;
636 success =
637 (add_hash_item
638 (Scr.Windows->bookmarks, AS_HASHABLE (tmp),
639 hd.vptr) == ASH_Success);
640 if (!success)
641 free (tmp);
642 LOCAL_DEBUG_OUT
643 ("Added Bookmark for window %p, ID=%8.8lX, -> \"%s\"", asw,
644 asw->w, bookmark);
645 }
646 }
647 return success;
648 }
649
650
bookmark2ASWindow(const char * bookmark)651 ASWindow *bookmark2ASWindow (const char *bookmark)
652 {
653 ASWindow *asw = NULL;
654 Bool success = False;
655 ASHashData hd;
656 hd.c32 = None;
657 if (bookmark) {
658 if (get_hash_item
659 (Scr.Windows->bookmarks, AS_HASHABLE (bookmark),
660 &(hd.vptr)) == ASH_Success) {
661 success = True;
662 asw = window2ASWindow (hd.c32);
663 }
664 #if defined(LOCAL_DEBUG) && !defined(NO_DEBUG_OUTPUT)
665 print_ashash (Scr.Windows->bookmarks, string_print);
666 #endif
667 }
668 LOCAL_DEBUG_OUT ("Window %p, ID=%8.8lX, %sfetched for bookmark \"%s\"",
669 asw, hd.c32, success ? "" : "not ", bookmark);
670 return asw;
671 }
672
pattern2ASWindow(const char * pattern)673 ASWindow *pattern2ASWindow (const char *pattern)
674 {
675 ASWindow *asw = bookmark2ASWindow (pattern);
676 if (asw == NULL) {
677 wild_reg_exp *wrexp = compile_wild_reg_exp (pattern);
678 if (wrexp != NULL) {
679 ASBiDirElem *e = LIST_START (Scr.Windows->clients);
680 while (e != NULL) {
681 asw = (ASWindow *) LISTELEM_DATA (e);
682 if (match_string_list (asw->hints->names, MAX_WINDOW_NAMES, wrexp)
683 == 0)
684 break;
685 else
686 asw = NULL;
687 LIST_GOTO_NEXT (e);
688 }
689 }
690 destroy_wild_reg_exp (wrexp);
691 }
692 return asw;
693 }
694
parse_semicolon_token(char * src,char * dst,int * len)695 char *parse_semicolon_token (char *src, char *dst, int *len)
696 {
697 int i = 0;
698 while (*src != '\0') {
699 if (*src == ':') {
700 if (*(src + 1) == ':')
701 ++src;
702 else
703 break;
704 }
705 dst[i] = *src;
706 ++i;
707 ++src;
708 }
709 dst[i] = '\0';
710 *len = i;
711 return (*src == ':') ? src + 1 : src;
712 }
713
complex_pattern2ASWindow(char * pattern)714 ASWindow *complex_pattern2ASWindow (char *pattern)
715 {
716 ASWindow *asw = NULL;
717 /* format : [<res_class>]:[<res_name>]:[[#<seq_no>]|<name>] */
718 LOCAL_DEBUG_OUT ("looking for window matchng pattern \"%s\"", pattern);
719 if (pattern && pattern[0]) {
720 wild_reg_exp *res_class_wrexp = NULL;
721 wild_reg_exp *res_name_wrexp = NULL;
722 int res_name_no = 1;
723 wild_reg_exp *name_wrexp = NULL;
724 ASBiDirElem *e = LIST_START (Scr.Windows->clients);
725 char *ptr = pattern;
726 char *tmp = safemalloc (strlen (pattern) + 1);
727 int tmp_len = 0;
728 Bool matches_reqired = 0;
729
730 ptr = parse_semicolon_token (ptr, tmp, &tmp_len);
731 LOCAL_DEBUG_OUT ("res_class pattern = \"%s\"", tmp);
732 if (tmp[0]) {
733 res_class_wrexp = compile_wild_reg_exp_sized (tmp, tmp_len);
734 ++matches_reqired;
735 ptr = parse_semicolon_token (ptr, tmp, &tmp_len);
736 LOCAL_DEBUG_OUT ("res_name pattern = \"%s\"", tmp);
737 if (tmp[0]) {
738 res_name_wrexp = compile_wild_reg_exp_sized (tmp, tmp_len);
739 ++matches_reqired;
740 ptr = parse_semicolon_token (ptr, tmp, &tmp_len);
741 LOCAL_DEBUG_OUT ("final pattern = \"%s\"", tmp);
742 if (tmp[0] == '#' && isdigit (tmp[1])) {
743 res_name_no = atoi (tmp + 1);
744 LOCAL_DEBUG_OUT ("res_name_no = %d", res_name_no);
745 } else if (tmp[0]) {
746 name_wrexp = compile_wild_reg_exp_sized (tmp, tmp_len);
747 ++matches_reqired;
748 }
749 } else {
750 res_name_wrexp = res_class_wrexp;
751 name_wrexp = res_class_wrexp;
752 }
753 }
754 free (tmp);
755
756 for (; e != NULL && (asw == NULL || res_name_no > 0);
757 LIST_GOTO_NEXT (e)) {
758 ASWindow *curr = (ASWindow *) LISTELEM_DATA (e);
759 int matches = 0;
760 LOCAL_DEBUG_OUT ("matching res_class \"%s\"",
761 curr->hints->res_class);
762 if (res_class_wrexp != NULL)
763 if (match_wild_reg_exp (curr->hints->res_class, res_class_wrexp) ==
764 0)
765 ++matches;
766 LOCAL_DEBUG_OUT ("matching res_name \"%s\"", curr->hints->res_name);
767 if (res_name_wrexp != NULL)
768 if (match_wild_reg_exp (curr->hints->res_name, res_name_wrexp) ==
769 0)
770 ++matches;
771 LOCAL_DEBUG_OUT ("matching name \"%s\"", curr->hints->names[0]);
772 if (name_wrexp != NULL)
773 if (match_wild_reg_exp (curr->hints->names[0], name_wrexp) == 0 ||
774 match_wild_reg_exp (curr->hints->icon_name, name_wrexp) == 0)
775 ++matches;
776
777 if (matches < matches_reqired)
778 continue;
779 asw = curr;
780 --res_name_no;
781 LOCAL_DEBUG_OUT ("matches = %d, res_name_no = %d, asw = %p", matches,
782 res_name_no, asw);
783 }
784
785 if (res_class_wrexp)
786 destroy_wild_reg_exp (res_class_wrexp);
787 if (res_name_wrexp != res_class_wrexp && res_name_wrexp)
788 destroy_wild_reg_exp (res_name_wrexp);
789 if (name_wrexp && name_wrexp != res_class_wrexp)
790 destroy_wild_reg_exp (name_wrexp);
791 if (res_name_no > 0)
792 asw = NULL; /* not found with requested seq no */
793 }
794 return asw;
795 }
796
797
on_dead_aswindow(Window w)798 Bool on_dead_aswindow (Window w)
799 {
800 ASWindow *asw = window2ASWindow (w);
801 if (asw) {
802 if (w == asw->w && asw->status != NULL) {
803 ASWIN_SET_FLAGS (asw, AS_Dead);
804 show_progress
805 ("marking client's window as destroyed for client \"%s\", window 0x%X",
806 ASWIN_NAME (asw), w);
807 return True;
808 }
809 }
810 return False;
811 }
812
813 /*******************************************************************************/
814 /* layer management */
815
get_aslayer(int layer,ASWindowList * list)816 ASLayer *get_aslayer (int layer, ASWindowList * list)
817 {
818 ASLayer *l = NULL;
819 if (list && list->layers) {
820 ASHashableValue hlayer = AS_HASHABLE (layer);
821 ASHashData hdata = { 0 };
822 if (get_hash_item (list->layers, hlayer, &hdata.vptr) != ASH_Success) {
823 l = safecalloc (1, sizeof (ASLayer));
824 if (add_hash_item (list->layers, hlayer, l) == ASH_Success) {
825 l->members = create_asvector (sizeof (ASWindow *));
826 l->layer = layer;
827 LOCAL_DEBUG_OUT ("added new layer %p(%d) to hash", l, layer);
828 } else {
829 free (l);
830 LOCAL_DEBUG_OUT ("failed to add new layer %p(%d) to hash", l,
831 layer);
832 l = NULL;
833 }
834 } else
835 l = hdata.vptr;
836 }
837 return l;
838 }
839
destroy_aslayer(ASHashableValue value,void * data)840 void destroy_aslayer (ASHashableValue value, void *data)
841 {
842 if (data) {
843 ASLayer *l = data;
844 LOCAL_DEBUG_OUT ("destroying layer %p(%d)", l, l->layer);
845 destroy_asvector (&(l->members));
846 free (data);
847 }
848 }
849
destroy_window_group(ASHashableValue value,void * data)850 void destroy_window_group (ASHashableValue value, void *data)
851 {
852 if (data) {
853 ASWindowGroup *g = data;
854 LOCAL_DEBUG_OUT
855 ("destroying window group %p(%lx), sm_client_id = \"%s\"", g,
856 g->leader, g->sm_client_id ? g->sm_client_id : "none");
857 if (g->sm_client_id)
858 free (g->sm_client_id);
859 destroy_asvector (&(g->members));
860 free (data);
861 }
862 }
863
864 /********************************************************************************/
865 /* ASWindow management */
866
find_group_lead_aswindow(Window id)867 ASWindow *find_group_lead_aswindow (Window id)
868 {
869 ASWindow *gl = window2ASWindow (id);
870 if (gl == NULL) { /* let's find previous window with the same group lead */
871 ASBiDirElem *curr = LIST_START (Scr.Windows->clients);
872 while (curr) {
873 ASWindow *asw = (ASWindow *) LISTELEM_DATA (curr);
874 if (asw->hints->group_lead == id) {
875 gl = asw;
876 break;
877 }
878 LIST_GOTO_NEXT (curr);
879 }
880 }
881 return gl;
882 }
883
add_member_to_group(ASWindow * asw)884 static void add_member_to_group (ASWindow * asw)
885 {
886 if (asw && asw->hints->group_lead) {
887 ASWindowGroup *g = window2ASWindowGroup (asw->hints->group_lead);
888 if (g == NULL) {
889 g = safecalloc (1, sizeof (ASWindowGroup));
890 g->leader = asw->hints->group_lead;
891 g->member_count++;
892 if (add_hash_item
893 (Scr.Windows->window_groups, AS_HASHABLE (g->leader),
894 g) != ASH_Success) {
895 free (g);
896 g = NULL;
897 }
898 read_string_property (g->leader, _XA_SM_CLIENT_ID,
899 &(g->sm_client_id));
900 }
901 if (g != NULL) {
902 #if 0
903 if (group_lead->group_members == NULL)
904 group_lead->group_members = create_asvector (sizeof (ASWindow *));
905 /* ATTN: Inserting pointer to a member into the beginning of the list */
906 vector_insert_elem (group_lead->group_members, &member, 1, NULL,
907 True);
908 #endif
909 }
910 asw->group = g;
911 }
912 }
913
add_aswindow_to_layer(ASWindow * asw,int layer)914 void add_aswindow_to_layer (ASWindow * asw, int layer)
915 {
916 if (!AS_ASSERT (asw) && asw->transient_owner == NULL) {
917 ASLayer *dst_layer = get_aslayer (layer, Scr.Windows);
918 /* inserting window into the top of the new layer */
919
920 LOCAL_DEBUG_OUT ("adding window %p to the top of the layer %p (%d)",
921 asw, dst_layer, layer);
922 /* ATTN: Inserting pointer to a member into the beginning of the list */
923 if (!AS_ASSERT (dst_layer))
924 vector_insert_elem (dst_layer->members, &asw, 1, NULL, True);
925 }
926 }
927
remove_aswindow_from_layer(ASWindow * asw,int layer)928 void remove_aswindow_from_layer (ASWindow * asw, int layer)
929 {
930 if (!AS_ASSERT (asw)) {
931 ASLayer *src_layer = get_aslayer (layer, Scr.Windows);
932 LOCAL_DEBUG_OUT ("removing window %p from layer %p (%d)", asw,
933 src_layer, layer);
934 if (!AS_ASSERT (src_layer)) {
935 LOCAL_DEBUG_OUT ("can be found at index %d",
936 vector_find_data (src_layer->members, &asw));
937 while (vector_find_data (src_layer->members, &asw) <
938 src_layer->members->used) {
939 vector_remove_elem (src_layer->members, &asw);
940 LOCAL_DEBUG_OUT ("after deletion can be found at index %d(used%d)",
941 vector_find_data (src_layer->members, &asw),
942 src_layer->members->used);
943 }
944 }
945 }
946 }
947
948
949
tie_aswindow(ASWindow * t)950 void tie_aswindow (ASWindow * t)
951 {
952 if (t->hints->transient_for != None) {
953 ASWindow *transient_owner = window2ASWindow (t->hints->transient_for);
954 if (transient_owner != NULL) { /* we want to find the topmost window */
955 while (transient_owner->transient_owner != NULL &&
956 transient_owner != transient_owner->transient_owner) {
957 if (ASWIN_GET_FLAGS (transient_owner->transient_owner, AS_Dead)) {
958 ASWindow *t = transient_owner;
959 if (t->transient_owner->transients != NULL)
960 vector_remove_elem (t->transient_owner->transients, &t);
961 t->transient_owner = NULL;
962 add_aswindow_to_layer (t, ASWIN_LAYER (t));
963 } else
964 transient_owner = transient_owner->transient_owner;
965 }
966
967 t->transient_owner = transient_owner;
968 if (transient_owner->transients == NULL)
969 transient_owner->transients =
970 create_asvector (sizeof (ASWindow *));
971 /* ATTN: Inserting pointer to a transient into the beginning of the list */
972 vector_insert_elem (transient_owner->transients, &t, 1, NULL, True);
973 }
974 }
975 add_member_to_group (t);
976 }
977
untie_aswindow(ASWindow * t)978 void untie_aswindow (ASWindow * t)
979 {
980 ASWindow **sublist;
981 int i;
982
983 if (t->transient_owner != NULL
984 && t->transient_owner->magic == MAGIC_ASWINDOW) {
985 if (t->transient_owner != NULL)
986 vector_remove_elem (t->transient_owner->transients, &t);
987 t->transient_owner = NULL;
988 }
989 if (t->transients && PVECTOR_USED (t->transients) > 0) {
990 sublist = PVECTOR_HEAD (ASWindow *, t->transients);
991
992 /* ATTN: this code reverses the order if add_aswindow_to_layer inserts into the beginnig of the list !!! */
993 /*for( i = 0 ; i < PVECTOR_USED(t->transients) ; ++i ) */
994 /* ATTN: this code keeps the order if add_member_to_group_lead inserts into the beginnig of the list !!! */
995 i = PVECTOR_USED (t->transients);
996 while (--i >= 0)
997 if (sublist[i] && sublist[i]->magic == MAGIC_ASWINDOW && sublist[i]->transient_owner == t) { /* we may need to delete this windows actually */
998 sublist[i]->transient_owner = NULL;
999 add_aswindow_to_layer (sublist[i], ASWIN_LAYER (sublist[i]));
1000 }
1001 }
1002 #if 0 /* Possibly need to remove us from group's list */
1003 if (t->group_lead && t->group_lead->magic == MAGIC_ASWINDOW) {
1004 if (t->group_lead->group_members)
1005 vector_remove_elem (t->group_lead->group_members, &t);
1006 t->group_lead = NULL;
1007 }
1008 if (t->group) {
1009 ASWindow *new_gl;
1010 sublist = PVECTOR_HEAD (ASWindow *, t->group_members);
1011 new_gl = sublist[0];
1012 new_gl->group_lead = NULL;
1013
1014 /* ATTN: this code reverses the order if add_member_to_group_lead inserts into the beginnig of the list !!! */
1015 /*for( i = 1 ; i < PVECTOR_USED(t->group_members) ; ++i ) */
1016 /* ATTN: this code keeps the order if add_member_to_group_lead inserts into the beginnig of the list !!! */
1017 i = PVECTOR_USED (t->group_members);
1018 while (--i >= 0)
1019 if (sublist[i] && sublist[i]->magic == MAGIC_ASWINDOW
1020 && sublist[i]->group_lead == t) {
1021 sublist[i]->group_lead = NULL;
1022 add_member_to_group_lead (new_gl, sublist[i]);
1023 }
1024 }
1025 #endif
1026 }
1027
enlist_aswindow(ASWindow * t)1028 Bool enlist_aswindow (ASWindow * t)
1029 {
1030 if (Scr.Windows == NULL)
1031 Scr.Windows = init_aswindow_list ();
1032
1033 append_bidirelem (Scr.Windows->clients, t);
1034
1035 /* ATTN: Inserting pointer to a member into the beginning of the circulate list */
1036 vector_insert_elem (Scr.Windows->circulate_list, &t, 1, NULL, True);
1037
1038 tie_aswindow (t);
1039
1040 /*fprintf (stderr, "Stacking order (%s): ptr = %p, transient_owner = %p, Layer = %d\n", __FUNCTION__, t, t->transient_owner, ASWIN_LAYER(t));
1041 */
1042 if (t->transient_owner == NULL)
1043 add_aswindow_to_layer (t, ASWIN_LAYER (t));
1044
1045 publish_aswindow_list (Scr.Windows, False);
1046
1047 return True;
1048 }
1049
delist_aswindow(ASWindow * t)1050 void delist_aswindow (ASWindow * t)
1051 {
1052 if (Scr.Windows == NULL)
1053 return;
1054
1055 if (AS_ASSERT (t))
1056 return;
1057 /* set desktop for window */
1058 if (t->w != Scr.Root)
1059 vector_remove_elem (Scr.Windows->circulate_list, &t);
1060
1061 if (ASWIN_GET_FLAGS (t, AS_Sticky))
1062 vector_remove_elem (Scr.Windows->sticky_list, &t);
1063
1064 remove_aswindow_from_layer (t, ASWIN_LAYER (t));
1065
1066 vector_remove_elem (Scr.Windows->stacking_order, &t);
1067
1068 untie_aswindow (t);
1069 discard_bidirelem (Scr.Windows->clients, t);
1070 publish_aswindow_list (Scr.Windows, False);
1071 }
1072
1073 /********************************************************************************************/
1074 /********************************************************************************************/
1075 /********************************************************************************************/
get_sorted_layers_vector(ASVector ** layers)1076 static int get_sorted_layers_vector (ASVector ** layers)
1077 {
1078 if (*layers == NULL)
1079 *layers = create_asvector (sizeof (ASLayer *));
1080 if (Scr.Windows->layers->items_num > (*layers)->allocated)
1081 realloc_vector (*layers, Scr.Windows->layers->items_num);
1082 /* Layers are sorted in descending order (see init_winlist) */
1083 return sort_hash_items (Scr.Windows->layers, NULL,
1084 (void **)VECTOR_HEAD_RAW (**layers), 0);
1085 }
1086
stack_transients(ASWindow * asw,ASVector * list)1087 static void stack_transients (ASWindow * asw, ASVector * list)
1088 {
1089 int tnum = PVECTOR_USED (asw->transients);
1090 LOCAL_DEBUG_OUT ("Client %lX has %d transients", asw->w, tnum);
1091 if (tnum > 0) { /* need to collect all the transients and stick them in front of us in order of creation */
1092 ASWindow **sublist = PVECTOR_HEAD (ASWindow *, asw->transients);
1093 int curr;
1094 for (curr = 0; curr < tnum; ++curr)
1095 if (!ASWIN_GET_FLAGS (sublist[curr], AS_Dead)) {
1096 if (vector_find_data (list, &sublist[curr]) >= PVECTOR_USED (list)) {
1097 LOCAL_DEBUG_OUT
1098 ("Adding transient #%d - %p, w = %lX, frame = %lX", curr,
1099 sublist[curr], sublist[curr]->w, sublist[curr]->frame);
1100 /* ATTN: Inserting pointer to a transient into the END of the stacking list */
1101 vector_insert_elem (list, &sublist[curr], 1, NULL, False);
1102 }
1103 }
1104 }
1105 }
1106
stack_layer_windows(ASLayer * layer,ASVector * list)1107 static inline void stack_layer_windows (ASLayer * layer, ASVector * list)
1108 {
1109 int k;
1110 ASWindow **members = PVECTOR_HEAD (ASWindow *, layer->members);
1111
1112 LOCAL_DEBUG_OUT ("layer %d, end_k = %d", layer->layer,
1113 PVECTOR_USED (layer->members));
1114 for (k = 0; k < PVECTOR_USED (layer->members); k++) {
1115 ASWindow *asw = members[k];
1116 if (!ASWIN_GET_FLAGS (asw, AS_Dead)) {
1117 LOCAL_DEBUG_OUT ("Group %p", asw->group);
1118 #if 0 /* TODO do we really need that ??? */
1119 if (asw->group) { /* transients for any window in the group go on top of non-transients in the group */
1120 ASWindow **sublist =
1121 PVECTOR_HEAD (ASWindow *, asw->group->members);
1122 int curr = PVECTOR_USED (asw->group->members);
1123 LOCAL_DEBUG_OUT ("Group members %d", curr);
1124 if (asw->group_lead->transients
1125 && !ASWIN_GET_FLAGS (asw->group_lead, AS_Dead))
1126 stack_transients (asw->group_lead, list);
1127
1128 /* ATTN: traversing group members in reverse order ????*/
1129 while (--curr >= 0) { /* most recent group member should have their transients above others */
1130 if (!ASWIN_GET_FLAGS (sublist[curr], AS_Dead)
1131 && sublist[curr]->transients)
1132 stack_transients (sublist[curr], list);
1133 }
1134 } else
1135 #endif
1136 if (asw->transients)
1137 stack_transients (asw, list);
1138
1139 LOCAL_DEBUG_OUT ("Adding client - %p, w = %lX, frame = %lX", asw,
1140 asw->w, asw->frame);
1141 /* ATTN: Inserting pointer to a window into the END of the stacking list */
1142 vector_insert_elem (list, &asw, 1, NULL, False);
1143
1144 /*fprintf (stderr, "\tStacking order append(%s) : id = 0x%8.8lX, ptr = %p, name = \"%s\", total = %d\n", __FUNCTION__, asw->w, asw, ASWIN_NAME(asw), PVECTOR_USED(list));
1145 */
1146
1147 }
1148 }
1149 }
1150
1151 static ASVector *__as_scratch_layers = NULL;
1152
free_scratch_layers_vector()1153 void free_scratch_layers_vector ()
1154 {
1155 if (__as_scratch_layers)
1156 destroy_asvector (&__as_scratch_layers);
1157 }
1158
get_scratch_layers_vector()1159 ASVector *get_scratch_layers_vector ()
1160 {
1161 if (__as_scratch_layers == NULL)
1162 __as_scratch_layers = create_asvector (sizeof (ASLayer *));
1163 else
1164 flush_vector (__as_scratch_layers);
1165
1166 return __as_scratch_layers;
1167 }
1168
1169
update_stacking_order()1170 void update_stacking_order ()
1171 {
1172 ASVector *layers = get_scratch_layers_vector ();
1173 unsigned long layers_in, i;
1174 ASLayer **l;
1175 ASVector *list = Scr.Windows->stacking_order;
1176
1177 flush_vector (list);
1178 if (Scr.Windows->clients->count == 0)
1179 return;
1180
1181 if ((layers_in = get_sorted_layers_vector (&layers)) == 0)
1182 return;
1183
1184 realloc_vector (list, Scr.Windows->clients->count);
1185 l = PVECTOR_HEAD (ASLayer *, layers);
1186 for (i = 0; i < layers_in; i++)
1187 stack_layer_windows (l[i], list);
1188
1189 /* fprintf (stderr, "updated Stacking order of %d windows. Clients count = %d, Layers count = %d. \n", PVECTOR_USED(list), Scr.Windows->clients->count, layers_in);
1190 */
1191
1192 }
1193
get_stacking_order_list(ASWindowList * list,int * stack_len_return)1194 static ASWindow **get_stacking_order_list (ASWindowList * list,
1195 int *stack_len_return)
1196 {
1197 int stack_len = PVECTOR_USED (list->stacking_order);
1198 if (stack_len == 0) {
1199 update_stacking_order ();
1200 stack_len = PVECTOR_USED (list->stacking_order);
1201 }
1202 *stack_len_return = stack_len;
1203 /****
1204 {
1205 int i;
1206 ASWindow **list = PVECTOR_HEAD(ASWindow*, Scr.Windows->stacking_order);
1207 fprintf (stderr, "Stacking order of %d windows: \n", stack_len);
1208 for ( i = 0 ; i < stack_len; ++i)
1209 fprintf (stderr, "\t%4.4d : id = 0x%8.8lX, ptr = %p, name = \"%s\"\n",
1210 i, list[i]->w, list[i], ASWIN_NAME(list[i]));
1211 }
1212 ****/
1213 return PVECTOR_HEAD (ASWindow *, Scr.Windows->stacking_order);
1214 }
1215
1216 static ASVector *__as_scratch_ids = NULL;
1217
free_scratch_ids_vector()1218 void free_scratch_ids_vector ()
1219 {
1220 if (__as_scratch_ids)
1221 destroy_asvector (&__as_scratch_ids);
1222 }
1223
get_scratch_ids_vector()1224 ASVector *get_scratch_ids_vector ()
1225 {
1226 if (__as_scratch_ids == NULL)
1227 __as_scratch_ids = create_asvector (sizeof (Window));
1228 else
1229 flush_vector (__as_scratch_ids);
1230
1231 if (Scr.Windows->clients->count + 2 > __as_scratch_ids->allocated)
1232 realloc_vector (__as_scratch_ids, Scr.Windows->clients->count + 2);
1233
1234 return __as_scratch_ids;
1235 }
1236
send_stacking_order(int desk)1237 void send_stacking_order (int desk)
1238 {
1239 if (Scr.Windows->clients->count > 0) {
1240 int i;
1241 int stack_len = 0;
1242 ASWindow **stack = get_stacking_order_list (Scr.Windows, &stack_len);
1243 ASVector *ids = get_scratch_ids_vector ();
1244
1245 for (i = 0; i < stack_len; ++i)
1246 vector_insert_elem (ids, &(stack[i]->w), 1, NULL, False);
1247
1248 SendStackingOrder (-1, M_STACKING_ORDER, desk, ids);
1249 }
1250 }
1251
apply_stacking_order(int desk)1252 void apply_stacking_order (int desk)
1253 {
1254 if (Scr.Windows->clients->count > 0) {
1255 int i;
1256 int stack_len = 0;
1257 ASWindow **stack = get_stacking_order_list (Scr.Windows, &stack_len);
1258 ASVector *ids = get_scratch_ids_vector ();
1259 Window cw = get_desktop_cover_window ();
1260 int windows_num;
1261
1262 if (cw != None && !get_flags (AfterStepState, ASS_Shutdown)) {
1263 LOCAL_DEBUG_OUT ("desktop cover id = 0x%lX", cw);
1264 vector_insert_elem (ids, &cw, 1, NULL, True);
1265 }
1266
1267 for (i = 0; i < stack_len; ++i)
1268 if (ASWIN_DESK (stack[i]) == Scr.CurrentDesk) {
1269 /* if window is not on root currently - stacking order fails with BadMatch */
1270 LOCAL_DEBUG_OUT ("name \"%s\", frame id = 0x%lX",
1271 ASWIN_NAME (stack[i]), stack[i]->frame);
1272 vector_insert_elem (ids, &(stack[i]->frame), 1, NULL, False);
1273 }
1274
1275 windows_num = PVECTOR_USED (ids);
1276 LOCAL_DEBUG_OUT ("Setting stacking order: windows_num = %d, ",
1277 windows_num);
1278 if (windows_num > 0) {
1279 Window *windows = PVECTOR_HEAD (Window, ids);
1280
1281 XRaiseWindow (dpy, windows[0]);
1282 if (windows_num > 1)
1283 XRestackWindows (dpy, windows, windows_num);
1284 XSync (dpy, False);
1285 }
1286 raise_scren_panframes (ASDefaultScr);
1287 XRaiseWindow (dpy, Scr.ServiceWin);
1288 }
1289 }
1290
publish_aswindow_list(ASWindowList * list,Bool stacking_only)1291 void publish_aswindow_list (ASWindowList * list, Bool stacking_only)
1292 {
1293 if (!get_flags (AfterStepState, ASS_Shutdown)) {
1294 int i;
1295 int stack_len = 0;
1296 ASWindow **stack = get_stacking_order_list (Scr.Windows, &stack_len);
1297 ASVector *ids = get_scratch_ids_vector ();
1298
1299 /* we maybe called from Destroy, in which case one of the clients may be
1300 delisted from main list, while still present in it's owner's transients list
1301 which is why we use +1 - This was happening for some clients who'd have
1302 recursive transients ( transient of a transient of a transient )
1303 Since we added check to unroll that sequence in tie_aswindow - problem had gone away,
1304 but lets keep on adding 1 just in case.
1305 */
1306 if (!stacking_only) {
1307 ASBiDirElem *curr = LIST_START (list->clients);
1308 while (curr) {
1309 ASWindow *asw = (ASWindow *) LISTELEM_DATA (curr);
1310 vector_insert_elem (ids, &(asw->w), 1, NULL, False);
1311 LIST_GOTO_NEXT (curr);
1312 }
1313 LOCAL_DEBUG_OUT
1314 ("Setting Client List property to include %d windows ",
1315 PVECTOR_USED (ids));
1316 set_clients_list (Scr.wmprops, PVECTOR_HEAD (Window, ids),
1317 PVECTOR_USED (ids));
1318 flush_vector (ids);
1319 }
1320
1321 i = stack_len;
1322 while (--i >= 0)
1323 vector_insert_elem (ids, &(stack[i]->w), 1, NULL, False);
1324
1325 set_stacking_order (Scr.wmprops, PVECTOR_HEAD (Window, ids),
1326 PVECTOR_USED (ids));
1327 }
1328 }
1329
restack_window_list(int desk)1330 void restack_window_list (int desk)
1331 {
1332 update_stacking_order ();
1333 send_stacking_order (desk);
1334 publish_aswindow_list (Scr.Windows, True);
1335 apply_stacking_order (desk);
1336 }
1337
find_topmost_client(int desk,int root_x,int root_y)1338 ASWindow *find_topmost_client (int desk, int root_x, int root_y)
1339 {
1340 if (Scr.Windows->clients->count > 0) {
1341 int i;
1342 int stack_len = 0;
1343 ASWindow **stack = get_stacking_order_list (Scr.Windows, &stack_len);
1344
1345 for (i = 0; i < stack_len; ++i) {
1346 register ASWindow *asw = stack[i];
1347 if (ASWIN_DESK (asw) == desk && !ASWIN_GET_FLAGS (asw, AS_Dead)) {
1348 register ASCanvas *fc = asw->frame_canvas;
1349 if (fc->root_x <= root_x && fc->root_y <= root_y &&
1350 fc->root_x + fc->width + fc->bw * 2 > root_x &&
1351 fc->root_y + fc->height + fc->bw * 2 > root_y)
1352 return asw;
1353 }
1354 }
1355 }
1356 return NULL;
1357 }
1358
1359
1360
1361 /*
1362 * we better have our own routine for changing window stacking order,
1363 * instead of simply passing it to X server, whenever client request
1364 * such thing, as we know more about window layout then server does
1365 */
1366 /* From Xlib reference :
1367 If a sibling and a stack_mode are specified, the window is restacked
1368 as follows:
1369 Above The window is placed just above the sibling.
1370 Below The window is placed just below the sibling.
1371 TopIf If any sibling occludes the window, the window is placed
1372 at the top of the stack.
1373 BottomIf If the window occludes any sibling, the window is placed
1374 at the bottom of the stack.
1375 Opposite If any sibling occludes the window, the window is placed
1376 at the top of the stack. If the window occludes any
1377 sibling, the window is placed at the bottom of the stack.
1378 */
1379 #define OCCLUSION_ABOVE -1
1380 #define OCCLUSION_NONE 0
1381 #define OCCLUSION_BELOW 1
1382
1383 /* Checks if rectangle above is at least partially obscuring client below */
is_rect_overlaping(ASRectangle * above,ASRectangle * below)1384 inline Bool is_rect_overlaping (ASRectangle * above, ASRectangle * below)
1385 {
1386 if (above == NULL)
1387 return False;
1388 if (below == NULL)
1389 return True;
1390
1391 return (above->x < below->x + below->width
1392 && above->x + above->width > below->x
1393 && above->y < below->y + below->height
1394 && above->y + above->height > below->y);
1395 }
1396
1397 inline Bool
is_status_overlaping(ASStatusHints * above,ASStatusHints * below)1398 is_status_overlaping (ASStatusHints * above, ASStatusHints * below)
1399 {
1400 if (above == NULL)
1401 return False;
1402 if (below == NULL)
1403 return True;
1404
1405 return (above->x < below->x + below->width
1406 && above->x + above->width > below->x
1407 && above->y < below->y + below->height
1408 && above->y + above->height > below->y);
1409 }
1410
is_canvas_overlaping(ASCanvas * above,ASCanvas * below)1411 inline Bool is_canvas_overlaping (ASCanvas * above, ASCanvas * below)
1412 {
1413 if (above == NULL)
1414 return False;
1415 if (below == NULL)
1416 return True;
1417 else {
1418 int below_left = below->root_x;
1419 int below_right = below_left + (int)below->width + (int)below->bw * 2;
1420 int above_left = above->root_x;
1421 int above_right = above_left + (int)above->width + (int)above->bw * 2;
1422 if (above_left < below_right && above_right > below_left) {
1423 int below_top = below->root_y;
1424 int below_bottom =
1425 below_top + (int)below->height + (int)below->bw * 2;
1426 int above_top = above->root_y;
1427 int above_bottom =
1428 above_top + (int)above->height + (int)above->bw * 2;
1429
1430 return (above_top < below_bottom && above_bottom > below_top);
1431 }
1432 }
1433 return False;
1434 }
1435
1436 #define IS_OVERLAPING(a,b) is_canvas_overlaping(a->frame_canvas,b->frame_canvas)
1437
is_overlaping_b(ASWindow * a,ASWindow * b)1438 static inline Bool is_overlaping_b (ASWindow * a, ASWindow * b)
1439 {
1440 int i;
1441 ASWindow **sublist;
1442 if (IS_OVERLAPING (a, b))
1443 return True;
1444
1445 if (b->transients) {
1446 sublist = PVECTOR_HEAD (ASWindow *, b->transients);
1447 for (i = 0; i < PVECTOR_USED (b->transients); ++i)
1448 if (!ASWIN_GET_FLAGS (sublist[i], AS_Dead))
1449 if (IS_OVERLAPING (a, sublist[i]))
1450 return True;
1451 }
1452 #if 0 /* TODO do we really need that ??? */
1453 if (b->group_members) {
1454 sublist = PVECTOR_HEAD (ASWindow *, b->group_members);
1455 for (i = 0; i < PVECTOR_USED (b->group_members); ++i)
1456 if (!ASWIN_GET_FLAGS (sublist[i], AS_Dead))
1457 if (IS_OVERLAPING (a, sublist[i]))
1458 return True;
1459 }
1460 #endif
1461 return False;
1462 }
1463
is_overlaping(ASWindow * a,ASWindow * b)1464 static inline Bool is_overlaping (ASWindow * a, ASWindow * b)
1465 {
1466 int i;
1467 ASWindow **sublist;
1468 if (is_overlaping_b (a, b))
1469 return True;
1470
1471 if (a->transients) {
1472 sublist = PVECTOR_HEAD (ASWindow *, a->transients);
1473 for (i = 0; i < PVECTOR_USED (a->transients); ++i)
1474 if (!ASWIN_GET_FLAGS (sublist[i], AS_Dead))
1475 if (is_overlaping_b (sublist[i], b))
1476 return True;
1477 }
1478 #if 0 /* TODO do we really need that ??? */
1479 if (a->group_members) {
1480 sublist = PVECTOR_HEAD (ASWindow *, a->group_members);
1481 for (i = 0; i < PVECTOR_USED (a->group_members); ++i)
1482 if (!ASWIN_GET_FLAGS (sublist[i], AS_Dead))
1483 if (is_overlaping_b (sublist[i], b))
1484 return True;
1485 }
1486 #endif
1487 return False;
1488 }
1489
is_window_obscured(ASWindow * above,ASWindow * below)1490 Bool is_window_obscured (ASWindow * above, ASWindow * below)
1491 {
1492 ASLayer *l;
1493 ASWindow **members;
1494
1495 if (above != NULL && below != NULL)
1496 return is_overlaping (above, below);
1497
1498 if (above == NULL && below != NULL) { /* checking if window "below" is completely obscured by any of the
1499 windows with the same layer above it in stacking order */
1500 register int i, end_i;
1501
1502 l = get_aslayer (ASWIN_LAYER (below), Scr.Windows);
1503 if (AS_ASSERT (l))
1504 return False;
1505
1506 end_i = l->members->used;
1507 members = VECTOR_HEAD (ASWindow *, *(l->members));
1508 for (i = 0; i < end_i; i++) {
1509 register ASWindow *t;
1510 if ((t = members[i]) == below) {
1511 return False;
1512 } else if (ASWIN_DESK (t) == ASWIN_DESK (below)) {
1513 if (is_overlaping (t, below)) {
1514 return True;
1515 }
1516 }
1517 }
1518 } else if (above != NULL) { /* checking if window "above" is completely obscuring any of the
1519 windows with the same layer below it in stacking order,
1520 or any of its transients !!! */
1521 register int i;
1522
1523 l = get_aslayer (ASWIN_LAYER (above), Scr.Windows);
1524 if (AS_ASSERT (l))
1525 return False;
1526 members = VECTOR_HEAD (ASWindow *, *(l->members));
1527 for (i = VECTOR_USED (*(l->members)) - 1; i >= 0; i--) {
1528 register ASWindow *t;
1529 if ((t = members[i]) == above)
1530 return False;
1531 else if (ASWIN_DESK (t) == ASWIN_DESK (above))
1532 if (is_overlaping (above, t))
1533 return True;
1534 }
1535 }
1536 return False;
1537 }
1538
restack_window(ASWindow * t,Window sibling_window,int stack_mode)1539 void restack_window (ASWindow * t, Window sibling_window, int stack_mode)
1540 {
1541 ASWindow *sibling = NULL;
1542 ASLayer *dst_layer = NULL, *src_layer;
1543 Bool above;
1544 int occlusion = OCCLUSION_NONE;
1545
1546 if (t == NULL)
1547 return;
1548
1549 if (t->transient_owner != NULL)
1550 t = t->transient_owner;
1551
1552 if (ASWIN_GET_FLAGS (t, AS_Dead))
1553 return;
1554
1555 LOCAL_DEBUG_CALLER_OUT ("%p,%lX,%d", t, sibling_window, stack_mode);
1556 src_layer = get_aslayer (ASWIN_LAYER (t), Scr.Windows);
1557
1558 if (sibling_window)
1559 if ((sibling = window2ASWindow (sibling_window)) != NULL) {
1560 if (sibling->transient_owner == t)
1561 sibling = NULL; /* can't restack relative to its own transient */
1562 else if (ASWIN_DESK (sibling) != ASWIN_DESK (t))
1563 sibling = NULL; /* can't restack relative to window on the other desk */
1564 else
1565 dst_layer = get_aslayer (ASWIN_LAYER (sibling), Scr.Windows);
1566 }
1567
1568 if (dst_layer == NULL)
1569 dst_layer = src_layer;
1570
1571 /* 2. do all the occlusion checks whithin our layer */
1572 if (stack_mode == TopIf) {
1573 LOCAL_DEBUG_OUT ("stack_mode = %s", "TopIf");
1574 if (is_window_obscured (sibling, t))
1575 occlusion = OCCLUSION_BELOW;
1576 } else if (stack_mode == BottomIf) {
1577 LOCAL_DEBUG_OUT ("stack_mode = %s", "BottomIf");
1578 if (is_window_obscured (t, sibling))
1579 occlusion = OCCLUSION_ABOVE;
1580 } else if (stack_mode == Opposite) {
1581 if (is_window_obscured (sibling, t)) {
1582 occlusion = OCCLUSION_BELOW;
1583 LOCAL_DEBUG_OUT ("stack_mode = opposite, occlusion = %s", "below");
1584 } else if (is_window_obscured (t, sibling)) {
1585 occlusion = OCCLUSION_ABOVE;
1586 LOCAL_DEBUG_OUT ("stack_mode = opposite, occlusion = %s", "above");
1587 } else {
1588 LOCAL_DEBUG_OUT ("stack_mode = opposite, occlusion = %s", "none");
1589 }
1590 }
1591 if (sibling)
1592 if (ASWIN_LAYER (sibling) != ASWIN_LAYER (t))
1593 occlusion = OCCLUSION_NONE;
1594
1595 if (!((stack_mode == TopIf && occlusion == OCCLUSION_BELOW) ||
1596 (stack_mode == BottomIf && occlusion == OCCLUSION_ABOVE) ||
1597 (stack_mode == Opposite && occlusion != OCCLUSION_NONE) ||
1598 stack_mode == Above || stack_mode == Below)) {
1599 return; /* nothing to be done */
1600 }
1601
1602 above = (stack_mode == Above || stack_mode == TopIf ||
1603 (stack_mode == Opposite && occlusion == OCCLUSION_BELOW));
1604
1605 if (stack_mode != Above && stack_mode != Below)
1606 sibling = NULL;
1607
1608 #if 0 /* TODO do we really need that ??? */
1609 if (t->group_members) {
1610 int k;
1611 int todo = 0;
1612 ASWindow **members = PVECTOR_HEAD (ASWindow *, src_layer->members);
1613 ASWindow **group_members =
1614 safemalloc (PVECTOR_USED (src_layer->members) *
1615 sizeof (ASWindow *));
1616 for (k = 0; k < PVECTOR_USED (src_layer->members); k++)
1617 if (members[k] != t && members[k]->group_lead == t)
1618 group_members[todo++] = members[k];
1619 for (k = 0; k < todo; ++k) {
1620 vector_remove_elem (src_layer->members, &group_members[k]);
1621 vector_insert_elem (dst_layer->members, &group_members[k], 1,
1622 sibling, above);
1623 if (sibling)
1624 sibling = group_members[k];
1625 }
1626 free (group_members);
1627 }
1628 #endif
1629 vector_remove_elem (src_layer->members, &t);
1630 vector_insert_elem (dst_layer->members, &t, 1, sibling, above);
1631
1632 t->last_restack_time = Scr.last_Timestamp;
1633 restack_window_list (ASWIN_DESK (t));
1634 }
1635
1636
1637 /*
1638 * Find next window in circulate csequence forward (dir 1) or backward (dir -1)
1639 * from specifyed window. when we reach top or bottom we are turning back
1640 * checking AutoRestart here to determine what to do when we have warped through
1641 * all the windows, and came back to start.
1642 */
1643
get_next_window(ASWindow * curr_win,char * action,int dir)1644 ASWindow *get_next_window (ASWindow * curr_win, char *action, int dir)
1645 {
1646 int end_i, i;
1647 ASWindow **clients;
1648
1649 if (Scr.Windows == NULL || curr_win == NULL)
1650 return NULL;
1651
1652 end_i = VECTOR_USED (*(Scr.Windows->circulate_list));
1653 clients = VECTOR_HEAD (ASWindow *, *(Scr.Windows->circulate_list));
1654
1655 if (end_i <= 1)
1656 return NULL;
1657 for (i = 0; i < end_i; ++i)
1658 if (clients[i] == curr_win) {
1659 if (i == 0 && dir < 0)
1660 return clients[end_i - 1];
1661 else if (i == end_i - 1 && dir > 0)
1662 return clients[0];
1663 else
1664 return clients[i + dir];
1665 }
1666
1667 return NULL;
1668 }
1669
1670 /********************************************************************
1671 * hides focus for the screen.
1672 **********************************************************************/
unset_focused_window()1673 void unset_focused_window()
1674 {
1675 if (Scr.Windows->focused) {
1676 if (Scr.Windows->focused->status) {
1677 ASWIN_CLEAR_FLAGS(Scr.Windows->focused, AS_Focused);
1678 set_client_state (Scr.Windows->focused->w, Scr.Windows->focused->status);
1679 }
1680 Scr.Windows->focused = NULL;
1681 }
1682 }
1683
hide_focus()1684 void hide_focus ()
1685 {
1686 if (get_flags (Scr.Feel.flags, ClickToFocus)
1687 && Scr.Windows->ungrabbed != NULL)
1688 grab_aswindow_buttons (Scr.Windows->ungrabbed, False);
1689
1690 LOCAL_DEBUG_CALLER_OUT ("CHANGE Scr.Windows->focused from %p to NULL",
1691 Scr.Windows->focused);
1692
1693 unset_focused_window();
1694 Scr.Windows->ungrabbed = NULL;
1695 XRaiseWindow (dpy, Scr.ServiceWin);
1696 LOCAL_DEBUG_OUT ("XSetInputFocus(window= %lX (service_win), time = %lu)",
1697 Scr.ServiceWin, Scr.last_Timestamp);
1698 XSetInputFocus (dpy, Scr.ServiceWin, RevertToParent, Scr.last_Timestamp);
1699 XSync (dpy, False);
1700 }
1701
1702 /********************************************************************
1703 * Sets the input focus to the indicated window.
1704 **********************************************************************/
commit_circulation()1705 void commit_circulation ()
1706 {
1707 ASWindow *asw = Scr.Windows->active;
1708 LOCAL_DEBUG_OUT ("circulation completed with active window being %p",
1709 asw);
1710 if (asw)
1711 if (vector_remove_elem (Scr.Windows->circulate_list, &asw) == 1) {
1712 LOCAL_DEBUG_OUT
1713 ("reinserting %p into the head of circulation list : ", asw);
1714 vector_insert_elem (Scr.Windows->circulate_list, &asw, 1, NULL,
1715 True);
1716 }
1717 Scr.Windows->warp_curr_index = -1;
1718 }
1719
autoraise_aswindow(void * data)1720 void autoraise_aswindow (void *data)
1721 {
1722 struct timeval tv;
1723 time_t msec = Scr.Feel.AutoRaiseDelay;
1724 time_t exp_sec =
1725 Scr.Windows->last_focus_change_sec + (msec * 1000 +
1726 Scr.Windows->
1727 last_focus_change_usec) /
1728 1000000;
1729 time_t exp_usec =
1730 (msec * 1000 + Scr.Windows->last_focus_change_usec) % 1000000;
1731
1732 if (Scr.Windows->focused
1733 && !get_flags (AfterStepState, ASS_HousekeepingMode)) {
1734 gettimeofday (&tv, NULL);
1735 if (exp_sec < tv.tv_sec ||
1736 (exp_sec == tv.tv_sec && exp_usec <= tv.tv_usec)) {
1737 /*fprintf (stderr, "Stacking order : Autoraise ptr = %p\n", Scr.Windows->focused);
1738 */
1739 RaiseObscuredWindow (Scr.Windows->focused);
1740 }
1741 }
1742 }
1743
focus_window(ASWindow * asw,Window w)1744 Bool focus_window (ASWindow * asw, Window w)
1745 {
1746 LOCAL_DEBUG_CALLER_OUT ("asw = %p, w = %lX", asw, w);
1747
1748 if (asw != NULL)
1749 if (get_flags (asw->hints->protocols, AS_DoesWmTakeFocus)
1750 && !ASWIN_GET_FLAGS (asw, AS_Dead))
1751 send_wm_protocol_request (asw->w, _XA_WM_TAKE_FOCUS,
1752 Scr.last_Timestamp);
1753
1754 ASSync (False);
1755 LOCAL_DEBUG_OUT ("focusing window %lX, client %lX, frame %lX, asw %p", w,
1756 asw->w, asw->frame, asw);
1757 /* using last_Timestamp here causes problems when moving between screens */
1758 /* at the same time using CurrentTime all the time seems to cause some apps to fail,
1759 * most noticeably GTK-perl
1760 *
1761 * Take 2: disabled CurrentTime altogether as it screwes up focus handling
1762 * Basically if you use CurrentTime when there are still bunch of Events
1763 * in the queue, those evens will not have any effect if you try setting
1764 * focus using their time, as X aready used its own friggin current time.
1765 * Don't ask, its a mess.
1766 * */
1767 if (w != None && ASWIN_HFLAGS (asw, AS_AcceptsFocus)) {
1768 Time t =
1769 /*(Scr.Windows->focused == NULL)?CurrentTime: */
1770 Scr.last_Timestamp;
1771 LOCAL_DEBUG_OUT ("XSetInputFocus(window= %lX, time = %lu)", w, t);
1772 XSetInputFocus (dpy, w, RevertToParent, t);
1773 }
1774
1775 ASSync (False);
1776 return (w != None);
1777 }
1778
autoraise_window(ASWindow * asw)1779 void autoraise_window (ASWindow * asw)
1780 {
1781 if (Scr.Feel.AutoRaiseDelay == 0) {
1782 /*fprintf (stderr, "Stacking order : Autoraise ptr = %p\n", asw);
1783 */
1784 RaiseWindow (asw);
1785 } else if (Scr.Feel.AutoRaiseDelay > 0) {
1786 struct timeval tv;
1787 LOCAL_DEBUG_OUT ("setting autoraise timer for asw %p",
1788 Scr.Windows->focused);
1789 gettimeofday (&tv, NULL);
1790 Scr.Windows->last_focus_change_sec = tv.tv_sec;
1791 Scr.Windows->last_focus_change_usec = tv.tv_usec;
1792 timer_new (Scr.Feel.AutoRaiseDelay, autoraise_aswindow,
1793 Scr.Windows->focused);
1794 }
1795
1796 }
1797
focus_aswindow(ASWindow * asw,Bool suppress_autoraise)1798 Bool focus_aswindow (ASWindow * asw, Bool suppress_autoraise)
1799 {
1800 Bool do_hide_focus = False;
1801 Bool do_nothing = False;
1802 Window w = None;
1803
1804 LOCAL_DEBUG_CALLER_OUT ("asw = %p", asw);
1805 if (asw) {
1806 if (!get_flags (AfterStepState, ASS_WarpingMode))
1807 if (vector_remove_elem (Scr.Windows->circulate_list, &asw) == 1)
1808 vector_insert_elem (Scr.Windows->circulate_list, &asw, 1, NULL,
1809 True);
1810
1811 #if 0
1812 /* ClickToFocus focus queue manipulation */
1813 if (asw != Scr.Focus)
1814 asw->focus_sequence = Scr.next_focus_sequence++;
1815 #endif
1816 do_hide_focus = (ASWIN_DESK (asw) != Scr.CurrentDesk) ||
1817 (ASWIN_GET_FLAGS (asw, AS_Iconic) &&
1818 asw->icon_canvas == NULL && asw->icon_title_canvas == NULL);
1819
1820 if (!ASWIN_FOCUSABLE (asw)) {
1821 if (Scr.Windows->focused != NULL
1822 && ASWIN_DESK (Scr.Windows->focused) == Scr.CurrentDesk)
1823 do_nothing = True;
1824 else
1825 do_hide_focus = True;
1826 }
1827 } else
1828 do_hide_focus = True;
1829
1830 if (Scr.NumberOfScreens > 1 && !do_hide_focus) {
1831 Window pointer_root;
1832 /* if pointer went onto another screen - we need to release focus
1833 * and let other screen's manager manage it from now on, untill
1834 * pointer comes back to our screen :*/
1835 ASQueryPointerRoot (&pointer_root, &w);
1836 if (pointer_root != Scr.Root) {
1837 do_hide_focus = True;
1838 do_nothing = False;
1839 }
1840 }
1841 if (!do_nothing && do_hide_focus)
1842 hide_focus ();
1843 if (do_nothing || do_hide_focus)
1844 return False;
1845
1846 if (get_flags (Scr.Feel.flags, ClickToFocus) && Scr.Windows->ungrabbed != asw) { /* need to grab all buttons for window that we are about to
1847 * unfocus */
1848 grab_aswindow_buttons (Scr.Windows->ungrabbed, False);
1849 grab_aswindow_buttons (asw, True);
1850 Scr.Windows->ungrabbed = asw;
1851 }
1852
1853 if (ASWIN_GET_FLAGS (asw, AS_Iconic)) { /* focus icon window or icon title of the iconic window */
1854 if (asw->icon_canvas && !ASWIN_GET_FLAGS (asw, AS_Dead)
1855 && validate_drawable (asw->icon_canvas->w, NULL, NULL) != None)
1856 w = asw->icon_canvas->w;
1857 else if (asw->icon_title_canvas)
1858 w = asw->icon_title_canvas->w;
1859 } else if (ASWIN_GET_FLAGS (asw, AS_Shaded)) { /* focus frame window of shaded clients */
1860 w = asw->frame;
1861 } else if (!ASWIN_GET_FLAGS (asw, AS_Dead)) { /* clients with visible top window can get focus directly: */
1862 w = asw->w;
1863 }
1864
1865 if (w == None)
1866 show_warning ("unable to focus window %lX for client %lX, frame %lX",
1867 w, asw->w, asw->frame);
1868 else if (!ASWIN_GET_FLAGS (asw, AS_Mapped))
1869 show_warning
1870 ("unable to focus unmapped window %lX for client %lX, frame %lX",
1871 w, asw->w, asw->frame);
1872 else if (ASWIN_GET_FLAGS (asw, AS_UnMapPending))
1873 show_warning
1874 ("unable to focus window %lX that is about to be unmapped for client %lX, frame %lX",
1875 w, asw->w, asw->frame);
1876 else {
1877 focus_window (asw, w);
1878
1879 LOCAL_DEBUG_CALLER_OUT ("CHANGE Scr.Windows->focused from %p to %p",
1880 Scr.Windows->focused, asw);
1881 unset_focused_window();
1882 Scr.Windows->focused = asw;
1883 ASWIN_SET_FLAGS(asw, AS_Focused);
1884 set_client_state (asw->w, asw->status);
1885
1886 if (!suppress_autoraise)
1887 autoraise_window (asw);
1888 }
1889
1890 XSync (dpy, False);
1891 return True;
1892 }
1893
1894 /*********************************************************************/
1895 /* focus management goes here : */
1896 /*********************************************************************/
1897 /* making window active : */
1898 /* handing over actuall focus : */
focus_active_window()1899 Bool focus_active_window ()
1900 {
1901 /* don't fiddle with focus if we are in housekeeping mode !!! */
1902 LOCAL_DEBUG_CALLER_OUT ("checking if we are in housekeeping mode (%ld)",
1903 get_flags (AfterStepState,
1904 ASS_HousekeepingMode));
1905 if (get_flags (AfterStepState, ASS_HousekeepingMode)
1906 || Scr.Windows->active == NULL)
1907 return False;
1908
1909 if (Scr.Windows->focused == Scr.Windows->active)
1910 return True; /* already has focus */
1911
1912 return focus_aswindow (Scr.Windows->active, FOCUS_ASW_CAN_AUTORAISE);
1913 }
1914
1915 /* second version of above : */
focus_next_aswindow(ASWindow * asw)1916 void focus_next_aswindow (ASWindow * asw)
1917 {
1918 ASWindow *new_focus = NULL;
1919
1920 if (get_flags (Scr.Feel.flags, ClickToFocus))
1921 new_focus = get_next_window (asw, NULL, 1);
1922 if (!activate_aswindow (new_focus, False, False))
1923 hide_focus ();
1924 }
1925
focus_prev_aswindow(ASWindow * asw)1926 void focus_prev_aswindow (ASWindow * asw)
1927 {
1928 ASWindow *new_focus = NULL;
1929
1930 if (get_flags (Scr.Feel.flags, ClickToFocus))
1931 new_focus = get_next_window (asw, NULL, -1);
1932 if (!activate_aswindow (new_focus, False, False))
1933 hide_focus ();
1934 }
1935
warp_to_aswindow(ASWindow * asw,Bool deiconify)1936 void warp_to_aswindow (ASWindow * asw, Bool deiconify)
1937 {
1938 if (asw)
1939 activate_aswindow (asw, True, deiconify);
1940 }
1941
1942 /*************************************************************************/
1943 /* end of the focus management */
1944 /*************************************************************************/
1945
1946
1947 /*********************************************************************************
1948 * Find next window in circulate csequence forward (dir 1) or backward (dir -1)
1949 * from specifyed window. when we reach top or bottom we are turning back
1950 * checking AutoRestart here to determine what to do when we have warped through
1951 * all the windows, and came back to start.
1952 *********************************************************************************/
warp_aswindow_list(ASWindowList * list,Bool backwards)1953 ASWindow *warp_aswindow_list (ASWindowList * list, Bool backwards)
1954 {
1955 register int i;
1956 register int dir = backwards ? -1 : 1;
1957 int end_i;
1958 ASWindow **clients;
1959 int loop_count = 0;
1960
1961 if (list == NULL)
1962 return NULL;
1963
1964 end_i = VECTOR_USED (*(list->circulate_list));
1965 clients = VECTOR_HEAD (ASWindow *, *(list->circulate_list));
1966
1967 if (end_i <= 1)
1968 return NULL;
1969
1970 if (list->warp_curr_index < 0) { /* need to initialize warping : */
1971 list->warp_curr_index = (dir > 0) ? 0 : end_i;
1972 list->warp_user_dir = dir;
1973 list->warp_init_dir = dir;
1974 list->warp_curr_dir = dir;
1975 } else if (dir == list->warp_user_dir) {
1976 dir = list->warp_curr_dir;
1977 } else {
1978 list->warp_user_dir = dir;
1979 /* user reversed direction - so do we : */
1980 dir = (list->warp_curr_dir > 0) ? -1 : 1;
1981 list->warp_curr_dir = dir;
1982 }
1983
1984 i = (dir > 0) ? 1 : end_i - 1; /*list->warp_curr_index + dir */
1985 do {
1986 LOCAL_DEBUG_OUT ("checking i(%d)->end_i(%d)->dir(%d)->AutoReverse(%d)",
1987 i, end_i, dir, Scr.Feel.AutoReverse);
1988 if (0 > i || i >= end_i) {
1989 if (Scr.Feel.AutoReverse == AST_OpenLoop)
1990 i = (dir < 0) ? end_i - 1 : 0;
1991 else if (Scr.Feel.AutoReverse == AST_ClosedLoop) {
1992 i = (dir < 0) ? 0 : end_i - 1;
1993 list->warp_curr_dir = dir = (dir < 0) ? 1 : -1;
1994 i += dir; /* we need to skip the one that was focused at the moment ! */
1995 } else
1996 return NULL;
1997 if (++loop_count >= 2)
1998 return NULL;
1999 }
2000
2001 list->warp_curr_index = i;
2002 if (!(ASWIN_HFLAGS (clients[i], AS_DontCirculate)) &&
2003 !(ASWIN_GET_FLAGS (clients[i], AS_Iconic)
2004 && get_flags (Scr.Feel.flags, CirculateSkipIcons))
2005 && (ASWIN_DESK (clients[i]) == Scr.CurrentDesk
2006 || get_flags (Scr.Feel.flags, AutoTabThroughDesks))) {
2007 return clients[i];
2008 }
2009 i += dir;
2010 } while (1);
2011 return NULL;
2012 }
2013
2014 /********************************************************************************/
2015 /* window list menus regeneration : */
2016 /********************************************************************************/
2017 static inline void
ASWindow2func_data(FunctionCode func,ASWindow * asw,FunctionData * fdata,char * scut,Bool icon_name)2018 ASWindow2func_data (FunctionCode func, ASWindow * asw,
2019 FunctionData * fdata, char *scut, Bool icon_name)
2020 {
2021 fdata->func = F_RAISE_IT;
2022 fdata->name =
2023 mystrdup (icon_name ? ASWIN_ICON_NAME (asw) : ASWIN_NAME (asw));
2024 if (!icon_name)
2025 fdata->name_encoding = ASWIN_NAME_ENCODING (asw);
2026 fdata->func_val[0] = (long)asw;
2027 fdata->func_val[1] = (long)asw->w;
2028 if (++(*scut) == ('9' + 1))
2029 (*scut) = 'A'; /* Next shortcut key */
2030 fdata->hotkey = (*scut);
2031 }
2032
2033 struct ASSortMenu_Aux {
2034 FunctionData fdata;
2035 void *ref_data;
2036 };
2037
compare_menu_func_data_name(const void * a,const void * b)2038 int compare_menu_func_data_name (const void *a, const void *b)
2039 {
2040 struct ASSortMenu_Aux *aa = *(struct ASSortMenu_Aux **)a;
2041 struct ASSortMenu_Aux *ab = *(struct ASSortMenu_Aux **)b;
2042
2043 /* LOCAL_DEBUG_OUT( "aa = %p, ab = %p", aa, ab ); */
2044 return strcmp (aa->fdata.name ? aa->fdata.name : "",
2045 ab->fdata.name ? ab->fdata.name : "");
2046 }
2047
2048
make_desk_winlist_menu(ASWindowList * list,int desk,int sort_order,Bool icon_name)2049 MenuData *make_desk_winlist_menu (ASWindowList * list, int desk,
2050 int sort_order, Bool icon_name)
2051 {
2052 char menu_name[256];
2053 MenuData *md;
2054 MenuDataItem *mdi;
2055 FunctionData fdata;
2056 char scut = '0'; /* Current short cut key */
2057 ASWindow **clients;
2058 int i, max_i;
2059 MinipixmapData minipixmaps[MINIPIXMAP_TypesNum] = { {0}, {0} };
2060
2061 if (list == NULL)
2062 return NULL;;
2063
2064 clients = PVECTOR_HEAD (ASWindow *, list->circulate_list);
2065 max_i = PVECTOR_USED (list->circulate_list);
2066
2067 if (IsValidDesk (desk))
2068 sprintf (menu_name, "Windows on Desktop #%d", desk);
2069 else
2070 sprintf (menu_name, "Windows on All Desktops");
2071
2072 if ((md = create_menu_data (menu_name)) == NULL)
2073 return NULL;
2074
2075 memset (&fdata, 0x00, sizeof (FunctionData));
2076 fdata.func = F_TITLE;
2077 fdata.name = mystrdup (menu_name);
2078 add_menu_fdata_item (md, &fdata, NULL);
2079
2080 if (sort_order == ASO_Alpha) {
2081 struct ASSortMenu_Aux **menuitems =
2082 safecalloc (max_i, sizeof (struct ASSortMenu_Aux *));
2083 int numitems = 0;
2084
2085 for (i = 0; i < max_i; ++i) {
2086 if ((ASWIN_DESK (clients[i]) == desk || !IsValidDesk (desk))
2087 && !ASWIN_HFLAGS (clients[i], AS_SkipWinList)) {
2088 menuitems[numitems] =
2089 safecalloc (1, sizeof (struct ASSortMenu_Aux));
2090 /* LOCAL_DEBUG_OUT( "menuitems[%d] = %p", numitems, menuitems[numitems] ); */
2091 menuitems[numitems]->ref_data = clients[i];
2092 ASWindow2func_data (F_RAISE_IT, clients[i],
2093 &(menuitems[numitems]->fdata), &scut,
2094 icon_name);
2095 ++numitems;
2096 }
2097 }
2098 qsort (menuitems, numitems, sizeof (struct ASSortMenu_Aux *),
2099 compare_menu_func_data_name);
2100 for (i = 0; i < numitems; ++i) {
2101 if (!get_flags (Scr.Feel.flags, WinListHideIcons))
2102 minipixmaps[MINIPIXMAP_Icon].image =
2103 get_client_icon_image (ASDefaultScr,
2104 ((ASWindow *) (menuitems[i]->ref_data))->hints, 32);
2105 else
2106 minipixmaps[MINIPIXMAP_Icon].image = NULL;
2107 if ((mdi =
2108 add_menu_fdata_item (md, &(menuitems[i]->fdata),
2109 &(minipixmaps[0]))) != NULL)
2110 set_flags (mdi->flags, MD_ScaleMinipixmapDown);
2111 safefree (menuitems[i]); /* scrubba-dub-dub */
2112 }
2113 safefree (menuitems);
2114 } else { /* if( sort_order == ASO_Circulation || sort_order == ASO_Stacking ) */
2115
2116 for (i = 0; i < max_i; ++i) {
2117 MinipixmapData minipixmaps[MINIPIXMAP_TypesNum] = { {0}
2118 , {0}
2119 };
2120 if ((ASWIN_DESK (clients[i]) == desk || !IsValidDesk (desk))
2121 && !ASWIN_HFLAGS (clients[i], AS_SkipWinList)) {
2122 ASWindow2func_data (F_RAISE_IT, clients[i], &fdata, &scut,
2123 icon_name);
2124 if (!get_flags (Scr.Feel.flags, WinListHideIcons))
2125 minipixmaps[MINIPIXMAP_Icon].image =
2126 get_client_icon_image (ASDefaultScr, clients[i]->hints, 32);
2127 else
2128 minipixmaps[MINIPIXMAP_Icon].image = NULL;
2129 if ((mdi =
2130 add_menu_fdata_item (md, &fdata, &(minipixmaps[0]))) != NULL)
2131 set_flags (mdi->flags, MD_ScaleMinipixmapDown);
2132 }
2133 }
2134 }
2135 return md;
2136 }
2137