1 /*
2  * Copyright (c) 2000 Sasha Vasko <sasha at aftercode.net>
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 #include "asapp.h"
24 #include <X11/cursorfont.h>
25 
26 
27 #include "afterstep.h"
28 #include "screen.h"
29 #include "asfeel.h"
30 #include "event.h"
31 
32 /**************************************************************************************
33  * ASFeel initialization and destruction code.
34  *************************************************************************************/
35 
init_asfeel(ASFeel * feel)36 void init_asfeel (ASFeel * feel)
37 {
38 	int i;
39 
40 	memset (feel, 0x00, sizeof (ASFeel));
41 	feel->magic = MAGIC_ASFEEL;
42 	feel->buttons2grab = 7;
43 	feel->AutoReverse = 0;
44 	feel->Xzap = 12;
45 	feel->Yzap = 12;
46 	feel->EdgeScrollX = feel->EdgeScrollY = -100000;
47 	feel->EdgeResistanceScroll = 0;
48 	feel->EdgeResistanceMove = 30;
49 	feel->EdgeAttractionScreen = 20;
50 	feel->EdgeAttractionWindow = 10;
51 	feel->EdgeResistanceDragScroll = -1;	/* by default should use EdgeResistanceScroll */
52 	feel->OpaqueMove = 5;
53 	feel->OpaqueResize = 5;
54 	feel->ClickTime = 500;				/* half a sec, for those sluggish folks */
55 	feel->AutoRaiseDelay = 0;
56 	feel->RaiseButtons = 0;
57 #ifdef __CYGWIN__
58 	feel->flags = DoHandlePageing;
59 #else
60 	feel->flags = DoHandlePageing /*|DontAnimateBackground */ ;
61 #endif
62 	feel->XorValue = (((unsigned long)1) << ASDefaultScr->d_depth) - 1;
63 
64 	feel->no_snaping_mod = ShiftMask | ControlMask;
65 
66 	feel->MouseButtonRoot = NULL;
67 	feel->FuncKeyRoot = NULL;
68 	feel->Popups = NULL;
69 	feel->ComplexFunctions = NULL;
70 	feel->ShadeAnimationSteps = 12;
71 
72 	feel->desk_cover_animation_type = 10;
73 	feel->desk_cover_animation_steps = 12;
74 
75 	feel->recent_submenu_items = 4;
76 
77 	for (i = 0; i < MAX_CURSORS; ++i)
78 		if (feel->cursors[i])
79 			feel->cursors[i] = ASDefaultScr->standard_cursors[i];
80 }
81 
create_asfeel()82 ASFeel *create_asfeel ()
83 {
84 	ASFeel *feel;
85 
86 	feel = safecalloc (1, sizeof (ASFeel));
87 	init_asfeel (feel);
88 	return feel;
89 }
90 
destroy_asfeel(ASFeel * feel,Bool reusable)91 void destroy_asfeel (ASFeel * feel, Bool reusable)
92 {
93 	if (feel) {
94 		if (feel->magic == MAGIC_ASFEEL) {
95 			register int i;
96 
97 			feel->magic = 0;
98 
99 			while (feel->MouseButtonRoot != NULL) {
100 				MouseButton *mb = feel->MouseButtonRoot;
101 
102 				feel->MouseButtonRoot = mb->NextButton;
103 				if (mb->fdata) {
104 					free_func_data (mb->fdata);
105 					free (mb->fdata);
106 				}
107 				free (mb);
108 			}
109 			while (feel->FuncKeyRoot != NULL) {
110 				FuncKey *fk = feel->FuncKeyRoot;
111 
112 				feel->FuncKeyRoot = fk->next;
113 				if (fk->name != NULL)
114 					free (fk->name);
115 				if (fk->fdata != NULL) {
116 					free_func_data (fk->fdata);
117 					free (fk->fdata);
118 				}
119 				free (fk);
120 			}
121 			for (i = 0; i < MAX_CURSORS; ++i)
122 				if (feel->cursors[i]
123 						&& feel->cursors[i] != ASDefaultScr->standard_cursors[i]) {
124 					XFreeCursor (dpy, feel->cursors[i]);
125 					feel->cursors[i] = None;
126 				}
127 			if (feel->Popups)
128 				destroy_ashash (&feel->Popups);
129 			if (feel->ComplexFunctions)
130 				destroy_ashash (&feel->ComplexFunctions);
131 			if (feel->window_boxes) {
132 				i = feel->window_boxes_num;
133 				while (--i >= 0)
134 					destroy_aswindow_box (&(feel->window_boxes[i]), True);
135 				free (feel->window_boxes);
136 			}
137 			destroy_string (&(feel->default_window_box_name));
138 		}
139 		if (!reusable)
140 			free (feel);
141 		else
142 			memset (feel, 0x00, sizeof (ASFeel));
143 	}
144 }
145 
apply_feel_cursor(Window w,ASFeel * feel,int cursor)146 void apply_feel_cursor (Window w, ASFeel * feel, int cursor)
147 {
148 	if (feel && cursor >= 0 && cursor < MAX_CURSORS && w != None)
149 		XDefineCursor (dpy, w, feel->cursors[cursor]);
150 }
151 
recolor_feel_cursors(ASFeel * feel,CARD32 fore,CARD32 back)152 void recolor_feel_cursors (ASFeel * feel, CARD32 fore, CARD32 back)
153 {
154 	if (feel) {
155 		int i;
156 		XColor xfore, xback;
157 
158 		xfore.red = ARGB32_RED16 (fore);
159 		xfore.green = ARGB32_GREEN16 (fore);
160 		xfore.blue = ARGB32_BLUE16 (fore);
161 		xback.red = ARGB32_RED16 (back);
162 		xback.green = ARGB32_GREEN16 (back);
163 		xback.blue = ARGB32_BLUE16 (back);
164 		for (i = 0; i < MAX_CURSORS; ++i)
165 			if (feel->cursors[i])
166 				XRecolorCursor (dpy, feel->cursors[i], &xfore, &xback);
167 	}
168 }
169 
170 
171 
apply_context_cursor(Window w,ASFeel * feel,unsigned long context)172 void apply_context_cursor (Window w, ASFeel * feel, unsigned long context)
173 {
174 	if (feel && context != 0 && w != None) {
175 		static Cursor last_cursor = None;
176 		static Window last_window = None;
177 		Cursor c = None;
178 
179 		switch (context) {
180 		case C_TITLE:
181 			c = feel->cursors[ASCUR_Title];
182 			break;
183 		case C_FrameN:
184 			c = feel->cursors[ASCUR_Top];
185 			break;
186 		case C_FrameE:
187 			c = feel->cursors[ASCUR_Right];
188 			break;
189 		case C_FrameS:
190 			c = feel->cursors[ASCUR_Bottom];
191 			break;
192 		case C_FrameW:
193 			c = feel->cursors[ASCUR_Left];
194 			break;
195 		case C_FrameNW:
196 			c = feel->cursors[ASCUR_TopLeft];
197 			break;
198 		case C_FrameNE:
199 			c = feel->cursors[ASCUR_TopRight];
200 			break;
201 		case C_FrameSW:
202 			c = feel->cursors[ASCUR_BottomLeft];
203 			break;
204 		case C_FrameSE:
205 			c = feel->cursors[ASCUR_BottomRight];
206 			break;
207 		default:
208 			if (get_flags (context, C_TButtonAll))
209 				c = feel->cursors[ASCUR_Sys];
210 			else if (get_flags (context, C_FRAME))
211 				c = feel->cursors[ASCUR_Move];
212 		}
213 
214 		if (c == None && get_flags (context, C_FRAME))
215 			c = feel->cursors[ASCUR_Move];
216 		LOCAL_DEBUG_OUT ("context %s, selected cursor %ld, window %lX",
217 										 context2text (context), c, w);
218 		if (last_window != w || last_cursor != c) {
219 			last_cursor = c;
220 			last_window = w;
221 			XDefineCursor (dpy, w, c);
222 		}
223 	}
224 }
225 
free_feel_cursors(ASFeel * feel)226 void free_feel_cursors (ASFeel * feel)
227 {
228 	register int i;
229 
230 	if (dpy == NULL || feel == NULL)
231 		return;
232 	/* free cursors */
233 	for (i = 0; i < MAX_CURSORS; i++)
234 		if (feel->cursors[i])
235 			XFreeCursor (dpy, feel->cursors[i]);
236 }
237 
check_feel_sanity(ASFeel * feel)238 void check_feel_sanity (ASFeel * feel)
239 {
240 	int i;
241 
242 	LOCAL_DEBUG_CALLER_OUT ("feel %p", feel);
243 	/* If no edge scroll line is provided in the setup file, use a default */
244 	if (feel->EdgeScrollX == -100000)
245 		feel->EdgeScrollX = 25;
246 	if (feel->EdgeScrollY == -100000)
247 		feel->EdgeScrollY = feel->EdgeScrollX;
248 
249 	if (get_flags (feel->flags, ClickToRaise) && (feel->AutoRaiseDelay == 0))
250 		feel->AutoRaiseDelay = -1;
251 
252 	/* if edgescroll >1000 and < 100000m
253 	 * wrap at edges of desktop (a "spherical" desktop) */
254 	feel->flags &= ~(EdgeWrapX | EdgeWrapY);
255 	if (feel->EdgeScrollX >= 1000) {
256 		feel->EdgeScrollX /= 1000;
257 		feel->flags |= EdgeWrapX;
258 	}
259 	if (feel->EdgeScrollY >= 1000) {
260 		feel->EdgeScrollY /= 1000;
261 		feel->flags |= EdgeWrapY;
262 	}
263 
264 	feel->EdgeScrollX =
265 			feel->EdgeScrollX * ASDefaultScr->MyDisplayWidth / 100;
266 	feel->EdgeScrollY =
267 			feel->EdgeScrollY * ASDefaultScr->MyDisplayHeight / 100;
268 
269 	if (feel->no_snaping_mod == 0)
270 		feel->no_snaping_mod = ShiftMask;
271 
272 	if (ASDefaultScr->VxMax == 0)
273 		clear_flags (feel->flags, EdgeWrapX);
274 	if (ASDefaultScr->VyMax == 0)
275 		clear_flags (feel->flags, EdgeWrapY);
276 
277 	i = feel->window_boxes_num;
278 	while (--i >= 0) {
279 		if (!get_flags (feel->window_boxes[i].area.flags, WidthValue)) {
280 			feel->window_boxes[i].area.width = ASDefaultScr->MyDisplayWidth;
281 			if (get_flags (feel->window_boxes[i].flags, ASA_Virtual))
282 				feel->window_boxes[i].area.width += ASDefaultScr->VxMax;
283 			feel->window_boxes[i].area.width -= feel->window_boxes[i].area.x;
284 		}
285 		if (!get_flags (feel->window_boxes[i].area.flags, HeightValue)) {
286 			feel->window_boxes[i].area.height = ASDefaultScr->MyDisplayHeight;
287 			if (get_flags (feel->window_boxes[i].flags, ASA_Virtual))
288 				feel->window_boxes[i].area.height += ASDefaultScr->VyMax;
289 			feel->window_boxes[i].area.height -= feel->window_boxes[i].area.y;
290 		}
291 		if (!get_flags (feel->window_boxes[i].flags, ASA_DesktopSet))
292 			feel->window_boxes[i].desk = INVALID_DESK;
293 
294 		if (!get_flags (feel->window_boxes[i].flags, ASA_MinLayerSet))
295 			feel->window_boxes[i].min_layer = AS_LayerLowest;
296 
297 		if (!get_flags (feel->window_boxes[i].flags, ASA_MaxLayerSet))
298 			feel->window_boxes[i].max_layer = AS_LayerHighest;
299 
300 		if (feel->default_window_box_name != NULL) {
301 			if (feel->window_boxes[i].name &&
302 					mystrcasecmp (feel->window_boxes[i].name,
303 												feel->default_window_box_name) == 0)
304 				feel->default_window_box = &(feel->window_boxes[i]);
305 		}
306 #if !defined(LOCAL_DEBUG) || defined(NO_DEBUG_OUTPUT)
307 		if (get_output_threshold () >= OUTPUT_LEVEL_DEBUG)
308 #endif
309 			print_window_box (&(feel->window_boxes[i]), i);
310 	}
311 
312 #if 1
313 	if (feel->default_window_box == NULL) {	/* build new default windowbox */
314 		i = feel->window_boxes_num;
315 		feel->window_boxes =
316 				realloc (feel->window_boxes, sizeof (ASWindowBox) * (i + 1));
317 		++(feel->window_boxes_num);
318 		feel->default_window_box = &(feel->window_boxes[i]);
319 		memset (feel->default_window_box, 0x00, sizeof (ASWindowBox));
320 		feel->default_window_box->name = mystrdup ("default");
321 		feel->default_window_box->area.width = ASDefaultScr->MyDisplayWidth;
322 		feel->default_window_box->area.height = ASDefaultScr->MyDisplayHeight;
323 		feel->default_window_box->main_strategy = ASP_Manual;
324 		feel->default_window_box->backup_strategy = ASP_Manual;
325 		/* we should enforce this one : */
326 		feel->default_window_box->desk = INVALID_DESK;
327 		feel->default_window_box->min_layer = AS_LayerLowest;
328 		feel->default_window_box->max_layer = AS_LayerHighest;
329 		if (get_flags (feel->deprecated_flags, FEEL_DEPRECATED_SmartPlacement)) {
330 			feel->default_window_box->main_strategy = ASP_SmartPlacement;
331 			if (get_flags
332 					(feel->deprecated_flags, FEEL_DEPRECATED_RandomPlacement))
333 				feel->default_window_box->backup_strategy = ASP_RandomPlacement;
334 		} else if (get_flags (feel->deprecated_flags, FEEL_DEPRECATED_RandomPlacement)) {	/* don't really want to use ManualPlacement if onlyRandomPlacement is requested */
335 			feel->default_window_box->main_strategy = ASP_RandomPlacement;
336 			feel->default_window_box->backup_strategy = ASP_RandomPlacement;
337 		}
338 #if !defined(LOCAL_DEBUG) || defined(NO_DEBUG_OUTPUT)
339 		if (get_output_threshold () >= OUTPUT_LEVEL_DEBUG)
340 #endif
341 			print_window_box (&(feel->window_boxes[i]), i);
342 	}
343 #endif
344 }
345 
346 /*************************************************************************
347  * WindowBox utility functions
348  *************************************************************************/
create_aswindow_box(const char * name)349 ASWindowBox *create_aswindow_box (const char *name)
350 {
351 	ASWindowBox *aswbox = safecalloc (1, sizeof (ASWindowBox));
352 
353 	aswbox->name = mystrdup (name);
354 	return aswbox;
355 }
356 
destroy_aswindow_box(ASWindowBox * aswbox,Bool reusable)357 void destroy_aswindow_box (ASWindowBox * aswbox, Bool reusable)
358 {
359 	if (aswbox) {
360 		if (aswbox->name)
361 			free (aswbox->name);
362 		if (!reusable)
363 			free (aswbox);
364 		else
365 			memset (aswbox, 0x00, sizeof (ASWindowBox));
366 	}
367 }
368 
print_window_box(ASWindowBox * aswbox,int index)369 void print_window_box (ASWindowBox * aswbox, int index)
370 {
371 	if (aswbox) {
372 		if (aswbox->name)
373 			fprintf (stderr, "WindowBox[%d].name = \"%s\";\n", index,
374 							 aswbox->name);
375 		fprintf (stderr, "WindowBox[%d].set_flags = 0x%lX;\n", index,
376 						 aswbox->set_flags);
377 		fprintf (stderr, "WindowBox[%d].flags = 0x%lX;\n", index,
378 						 aswbox->flags);
379 		fprintf (stderr, "WindowBox[%d].area.flags = 0x%X;\n", index,
380 						 aswbox->area.flags);
381 		fprintf (stderr, "WindowBox[%d].area.geometry = %dx%d%+d%+d;\n", index,
382 						 aswbox->area.width, aswbox->area.height, aswbox->area.x,
383 						 aswbox->area.y);
384 		fprintf (stderr, "WindowBox[%d].min_size = %dx%d;\n", index,
385 						 aswbox->min_width, aswbox->min_height);
386 		fprintf (stderr, "WindowBox[%d].max_size = %dx%d;\n", index,
387 						 aswbox->max_width, aswbox->max_height);
388 		fprintf (stderr, "WindowBox[%d].main_strategy = %d;\n", index,
389 						 aswbox->main_strategy);
390 		fprintf (stderr, "WindowBox[%d].backup_strategy = %d;\n", index,
391 						 aswbox->backup_strategy);
392 		fprintf (stderr, "WindowBox[%d].desk = %d;\n", index, aswbox->desk);
393 		fprintf (stderr, "WindowBox[%d].min_layer = %d;\n", index,
394 						 aswbox->min_layer);
395 		fprintf (stderr, "WindowBox[%d].max_layer = %d;\n", index,
396 						 aswbox->max_layer);
397 	}
398 }
399 
find_window_box(ASFeel * feel,const char * name)400 ASWindowBox *find_window_box (ASFeel * feel, const char *name)
401 {
402 	if (feel && name) {
403 		int i = feel->window_boxes_num;
404 		ASWindowBox *aswbox = &(feel->window_boxes[0]);
405 
406 		while (--i >= 0)
407 			if (mystrcasecmp (aswbox[i].name, name) == 0)
408 				return &(aswbox[i]);
409 	}
410 	return NULL;
411 }
412 
413 /*************************************************************************
414  * Menus :
415  *************************************************************************/
menu_data_destroy(ASHashableValue value,void * data)416 void menu_data_destroy (ASHashableValue value, void *data)
417 {
418 	MenuData *md = data;
419 
420 #ifdef DEBUG_ALLOCS
421 	LOCAL_DEBUG_CALLER_OUT ("menu_data_destroy(\"%s\", %p)", (char *)value,
422 													data);
423 #endif
424 	if ((char *)value)
425 		free ((char *)value);
426 	if (md) {
427 		if (md->magic == MAGIC_MENU_DATA) {
428 			if (md->name == (char *)value)
429 				md->name = NULL;
430 			destroy_menu_data (&md);
431 		}
432 	}
433 }
434 
init_list_of_menus(ASHashTable ** list,Bool force)435 void init_list_of_menus (ASHashTable ** list, Bool force)
436 {
437 	if (list == NULL)
438 		return;
439 
440 	if (force && *list != NULL)
441 		destroy_ashash (list);
442 
443 	if (*list == NULL) {
444 		*list =
445 				create_ashash (0, casestring_hash_value, casestring_compare,
446 											 menu_data_destroy);
447 		LOCAL_DEBUG_OUT ("created the list of Popups %p", *list);
448 	}
449 }
450