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