1 /*
2 * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3 * Copyright (C) 2004-2020 Kim Woelders
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies of the Software, its documentation and marketing & publicity
14 * materials, and acknowledgment shall be given in the documentation, materials
15 * and software packages that this Software was used.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24 #include "E.h"
25 #include "borders.h"
26 #include "desktops.h"
27 #include "dialog.h"
28 #include "ewins.h"
29 #include "file.h"
30 #include "groups.h"
31 #include "ipc.h"
32 #include "list.h"
33 #include "settings.h"
34 #include "snaps.h"
35 #include "timers.h"
36 #include "xwin.h"
37
38 struct _snapshot {
39 dlist_t list;
40 char *name;
41 char *win_title;
42 char *win_name;
43 char *win_class;
44 char *win_role;
45 EX_Window win;
46 EWin *used;
47 unsigned int startup_id;
48 char track_changes;
49 unsigned int match_flags;
50 unsigned int use_flags;
51
52 char *border_name;
53 int desktop;
54 int area_x, area_y;
55 int x, y;
56 int w, h;
57 int layer;
58 char sticky;
59 char shaded;
60 unsigned int flags[2];
61 char *cmd;
62 int *groups;
63 int num_groups;
64 char skiptask;
65 char skipfocus;
66 char skipwinlist;
67 #if USE_COMPOSITE
68 int opacity;
69 int focused_opacity;
70 char shadow;
71 #endif
72 };
73
74 static LIST_HEAD(ss_list);
75 static Timer *ss_timer = NULL;
76
77 static Snapshot *
_SnapCreate(const char * name)78 _SnapCreate(const char *name)
79 {
80 Snapshot *sn;
81
82 sn = ECALLOC(Snapshot, 1);
83 if (!sn)
84 return NULL;
85
86 LIST_APPEND(Snapshot, &ss_list, sn);
87
88 sn->name = Estrdup(name);
89
90 return sn;
91 }
92
93 static void
_SnapDestroy(Snapshot * sn)94 _SnapDestroy(Snapshot * sn)
95 {
96 LIST_REMOVE(Snapshot, &ss_list, sn);
97
98 if (sn->used)
99 sn->used->snap = NULL;
100
101 Efree(sn->name);
102 Efree(sn->win_title);
103 Efree(sn->win_name);
104 Efree(sn->win_class);
105 Efree(sn->win_role);
106 Efree(sn->border_name);
107 Efree(sn->cmd);
108 Efree(sn->groups);
109
110 Efree(sn);
111 }
112
113 /*
114 * Stupid hack to fix apps that set WM_WINDOW_ROLE to
115 * a <name>-<pid>-<something>-<time> like thing.
116 * Is this even ICCCM compliant?
117 */
118 static char *
_ParseRole(const char * role,char * buf,int len)119 _ParseRole(const char *role, char *buf, int len)
120 {
121 int l1, l2;
122
123 if (!role)
124 return NULL;
125
126 l1 = strlen(role);
127 if (l1 >= len)
128 l1 = len - 1;
129
130 for (l2 = l1; l2 > 0; l2--)
131 {
132 if (role[l2 - 1] != '-' &&
133 !(role[l2 - 1] >= '0' && role[l2 - 1] <= '9'))
134 break;
135 }
136 if (l1 - l2 > 8)
137 l1 = l2;
138 memcpy(buf, role, l1);
139 buf[l1] = '\0';
140
141 return buf;
142 }
143
144 #define SEQ(s1, s2) ((s1) && (s2) && !strcmp(s1, s2))
145
146 static int
_SnapEwinMatch(const Snapshot * sn,const EWin * ewin)147 _SnapEwinMatch(const Snapshot * sn, const EWin * ewin)
148 {
149 char buf[256], *s;
150
151 /* Don't allow matching anything */
152 if (!sn->match_flags)
153 return 0;
154
155 if (ewin->state.identified)
156 return sn->win == EwinGetClientXwin(ewin);
157
158 if (sn->startup_id && !sn->cmd)
159 return 0;
160
161 if (sn->match_flags & SNAP_MATCH_TITLE
162 && !SEQ(sn->win_title, EwinGetIcccmName(ewin)))
163 return 0;
164
165 if (sn->match_flags & SNAP_MATCH_NAME
166 && !SEQ(sn->win_name, EwinGetIcccmCName(ewin)))
167 return 0;
168
169 if (sn->match_flags & SNAP_MATCH_CLASS
170 && !SEQ(sn->win_class, EwinGetIcccmClass(ewin)))
171 return 0;
172
173 if (sn->match_flags & SNAP_MATCH_ROLE)
174 {
175 s = _ParseRole(ewin->icccm.wm_role, buf, sizeof(buf));
176 if (!SEQ(sn->win_role, s))
177 return 0;
178 }
179
180 /* Match! */
181 return 1;
182 }
183
184 static int
_SnapEwinFindMatchCmd(const void * data,const void * match)185 _SnapEwinFindMatchCmd(const void *data, const void *match)
186 {
187 const Snapshot *sn = (Snapshot *) data;
188 const EWin *ewin = (EWin *) match;
189
190 return sn->used ||
191 !(sn->startup_id && SEQ(sn->cmd, ewin->icccm.wm_command) &&
192 _SnapEwinMatch(sn, ewin));
193 }
194
195 static int
_SnapEwinFindMatch(const void * data,const void * match)196 _SnapEwinFindMatch(const void *data, const void *match)
197 {
198 const Snapshot *sn = (Snapshot *) data;
199 const EWin *ewin = (EWin *) match;
200
201 return sn->used || !_SnapEwinMatch(sn, ewin);
202 }
203
204 /* find a snapshot state that applies to this ewin */
205 static Snapshot *
_SnapEwinFind(EWin * ewin)206 _SnapEwinFind(EWin * ewin)
207 {
208 Snapshot *sn;
209
210 if (ewin->snap)
211 return ewin->snap;
212
213 if (LIST_IS_EMPTY(&ss_list))
214 return NULL;
215
216 /* If exec'ed by snap try matching command exactly */
217 sn = LIST_FIND(Snapshot, &ss_list, _SnapEwinFindMatchCmd, ewin);
218 if (sn && sn->startup_id > 0)
219 {
220 /* Assuming we were started by snap */
221 sn->startup_id = 0; /* Only the first time */
222 ewin->state.snapstarted = 1;
223 }
224
225 if (!sn)
226 sn = LIST_FIND(Snapshot, &ss_list, _SnapEwinFindMatch, ewin);
227
228 if (sn && !(sn->match_flags & SNAP_MATCH_MULTIPLE))
229 {
230 sn->used = ewin;
231 ewin->snap = sn;
232 }
233
234 return sn;
235 }
236
237 #define ST(s) ((s) ? (s) : "")
238
239 /* find a snapshot state that applies to this ewin Or if that doesnt exist */
240 /* create a new one */
241 static Snapshot *
_SnapEwinGet(EWin * ewin,unsigned int match_flags)242 _SnapEwinGet(EWin * ewin, unsigned int match_flags)
243 {
244 Snapshot *sn;
245 char buf[1024], *s;
246
247 sn = _SnapEwinFind(ewin);
248 if (sn)
249 return sn;
250
251 if ((match_flags & SNAP_MATCH_TITLE) && !EwinGetIcccmName(ewin))
252 match_flags ^= SNAP_MATCH_TITLE;
253 if ((match_flags & SNAP_MATCH_NAME) && !EwinGetIcccmCName(ewin))
254 match_flags ^= SNAP_MATCH_NAME;
255 if ((match_flags & SNAP_MATCH_CLASS) && !EwinGetIcccmClass(ewin))
256 match_flags ^= SNAP_MATCH_CLASS;
257 if ((match_flags & SNAP_MATCH_ROLE) && !ewin->icccm.wm_role)
258 match_flags ^= SNAP_MATCH_ROLE;
259 if (match_flags == 0)
260 {
261 if (!EwinGetIcccmName(ewin))
262 return NULL;
263 match_flags = SNAP_MATCH_TITLE;
264 }
265
266 sn = _SnapCreate(NULL);
267 if (!sn)
268 return NULL;
269
270 sn->match_flags = match_flags;
271 if (match_flags & SNAP_MATCH_TITLE)
272 sn->win_title = Estrdup(EwinGetIcccmName(ewin));
273 if (match_flags & SNAP_MATCH_NAME)
274 sn->win_name = Estrdup(EwinGetIcccmCName(ewin));
275 if (match_flags & SNAP_MATCH_CLASS)
276 sn->win_class = Estrdup(EwinGetIcccmClass(ewin));
277 if (match_flags & SNAP_MATCH_ROLE)
278 {
279 s = _ParseRole(ewin->icccm.wm_role, buf, sizeof(buf));
280 sn->win_role = Estrdup(s);
281 }
282
283 /* Set the snap name. Has no particular significance. */
284 if ((sn->win_name || sn->win_class) && sn->win_role)
285 Esnprintf(buf, sizeof(buf), "%s.%s:%s", ST(sn->win_name),
286 ST(sn->win_class), sn->win_role);
287 else if (sn->win_name || sn->win_class)
288 Esnprintf(buf, sizeof(buf), "%s.%s", ST(sn->win_name), ST(sn->win_class));
289 else if (sn->win_title)
290 Esnprintf(buf, sizeof(buf), "TITLE.%s", sn->win_title);
291 else /* We should not go here */
292 Esnprintf(buf, sizeof(buf), "TITLE.%s", EwinGetIcccmName(ewin));
293 sn->name = Estrdup(buf);
294
295 if (!(sn->match_flags & SNAP_MATCH_MULTIPLE))
296 {
297 sn->used = ewin;
298 ewin->snap = sn;
299 }
300
301 return sn;
302 }
303
304 /* record info about this Ewin's attributes */
305
306 static void
_SnapUpdateEwinBorder(Snapshot * sn,const EWin * ewin)307 _SnapUpdateEwinBorder(Snapshot * sn, const EWin * ewin)
308 {
309 EFREE_DUP(sn->border_name, BorderGetName(ewin->normal_border));
310 }
311
312 static void
_SnapUpdateEwinDesktop(Snapshot * sn,const EWin * ewin)313 _SnapUpdateEwinDesktop(Snapshot * sn, const EWin * ewin)
314 {
315 sn->desktop = EoGetDeskNum(ewin);
316 }
317
318 static void
_SnapUpdateEwinSize(Snapshot * sn,const EWin * ewin)319 _SnapUpdateEwinSize(Snapshot * sn, const EWin * ewin)
320 {
321 sn->w = ewin->client.w;
322 sn->h = ewin->client.h;
323 }
324
325 static void
_SnapUpdateEwinLocation(Snapshot * sn,const EWin * ewin)326 _SnapUpdateEwinLocation(Snapshot * sn, const EWin * ewin)
327 {
328 int ax, ay;
329
330 sn->x = EoGetX(ewin);
331 sn->y = EoGetY(ewin);
332 sn->area_x = ewin->area_x;
333 sn->area_y = ewin->area_y;
334 if (!EoIsSticky(ewin))
335 {
336 DeskGetArea(EoGetDesk(ewin), &ax, &ay);
337 sn->x += ((ax - sn->area_x) * WinGetW(VROOT));
338 sn->y += ((ay - sn->area_y) * WinGetH(VROOT));
339 }
340 }
341
342 static void
_SnapUpdateEwinLayer(Snapshot * sn,const EWin * ewin)343 _SnapUpdateEwinLayer(Snapshot * sn, const EWin * ewin)
344 {
345 sn->layer = EoGetLayer(ewin);
346 }
347
348 static void
_SnapUpdateEwinSticky(Snapshot * sn,const EWin * ewin)349 _SnapUpdateEwinSticky(Snapshot * sn, const EWin * ewin)
350 {
351 sn->sticky = EoIsSticky(ewin);
352 }
353
354 static void
_SnapUpdateEwinShade(Snapshot * sn,const EWin * ewin)355 _SnapUpdateEwinShade(Snapshot * sn, const EWin * ewin)
356 {
357 sn->shaded = ewin->state.shaded;
358 }
359
360 static void
_SnapUpdateEwinSkipLists(Snapshot * sn,const EWin * ewin)361 _SnapUpdateEwinSkipLists(Snapshot * sn, const EWin * ewin)
362 {
363 sn->skiptask = ewin->props.skip_ext_task;
364 sn->skipwinlist = ewin->props.skip_winlist;
365 sn->skipfocus = ewin->props.skip_focuslist;
366 }
367
368 static void
_SnapUpdateEwinFlags(Snapshot * sn,const EWin * ewin)369 _SnapUpdateEwinFlags(Snapshot * sn, const EWin * ewin)
370 {
371 EwinFlagsEncode(ewin, sn->flags);
372 }
373
374 static void
_SnapUpdateEwinCmd(Snapshot * sn,const EWin * ewin)375 _SnapUpdateEwinCmd(Snapshot * sn, const EWin * ewin)
376 {
377 if (ewin->icccm.wm_machine &&
378 strcmp(ewin->icccm.wm_machine, Mode.wm.machine_name))
379 return;
380
381 EFREE_DUP(sn->cmd, ewin->icccm.wm_command);
382 }
383
384 static void
_SnapUpdateEwinGroups(Snapshot * sn,const EWin * ewin,char onoff)385 _SnapUpdateEwinGroups(Snapshot * sn, const EWin * ewin, char onoff)
386 {
387 EWin **gwins, *ewin2;
388 int i, j, num;
389
390 if (!ewin)
391 return;
392
393 if (!ewin->groups)
394 {
395 EFREE_NULL(sn->groups);
396 sn->num_groups = 0;
397 return;
398 }
399
400 gwins =
401 ListWinGroupMembersForEwin(ewin, GROUP_ACTION_ANY, Mode.nogroup, &num);
402 for (i = 0; i < num; i++)
403 {
404 ewin2 = gwins[i];
405
406 if (onoff)
407 {
408 if (!ewin2->groups)
409 continue;
410
411 sn = ewin2->snap;
412 if (!sn)
413 sn = _SnapEwinGet(ewin2, SNAP_MATCH_DEFAULT);
414 if (!sn)
415 continue;
416
417 sn->num_groups = ewin2->num_groups;
418 EFREE_SET(sn->groups, EMALLOC(int, ewin2->num_groups));
419
420 for (j = 0; j < ewin2->num_groups; j++)
421 sn->groups[j] = GroupGetIndex(ewin2->groups[j]);
422 }
423 else
424 {
425 if (ewin->snap)
426 {
427 sn = ewin2->snap;
428 if (sn)
429 {
430 EFREE_NULL(sn->groups);
431 sn->num_groups = 0;
432 }
433 }
434 }
435 }
436 Efree(gwins);
437 }
438
439 #if USE_COMPOSITE
440
441 static void
_SnapUpdateEwinOpacity(Snapshot * sn,const EWin * ewin)442 _SnapUpdateEwinOpacity(Snapshot * sn, const EWin * ewin)
443 {
444 sn->opacity = OpacityToPercent(ewin->props.opacity);
445 sn->focused_opacity = OpacityToPercent(ewin->props.focused_opacity);
446 }
447
448 static void
_SnapUpdateEwinShadow(Snapshot * sn,const EWin * ewin)449 _SnapUpdateEwinShadow(Snapshot * sn, const EWin * ewin)
450 {
451 sn->shadow = EoGetShadow(ewin);
452 }
453
454 #endif
455
456 static void
_SnapUpdateEwin(Snapshot * sn,const EWin * ewin,unsigned int flags)457 _SnapUpdateEwin(Snapshot * sn, const EWin * ewin, unsigned int flags)
458 {
459 /* FIXME - We should check if anything is actually changed */
460
461 if (flags & SNAP_USE_BORDER)
462 _SnapUpdateEwinBorder(sn, ewin);
463 if (flags & SNAP_USE_COMMAND)
464 _SnapUpdateEwinCmd(sn, ewin);
465 if (flags & SNAP_USE_DESK)
466 _SnapUpdateEwinDesktop(sn, ewin);
467 if (flags & SNAP_USE_POS)
468 _SnapUpdateEwinLocation(sn, ewin);
469 if (flags & SNAP_USE_SIZE)
470 _SnapUpdateEwinSize(sn, ewin);
471 if (flags & SNAP_USE_LAYER)
472 _SnapUpdateEwinLayer(sn, ewin);
473 if (flags & SNAP_USE_STICKY)
474 _SnapUpdateEwinSticky(sn, ewin);
475 if (flags & SNAP_USE_SHADED)
476 _SnapUpdateEwinShade(sn, ewin);
477 if (flags & SNAP_USE_SKIP_LISTS)
478 _SnapUpdateEwinSkipLists(sn, ewin);
479 if (flags & SNAP_USE_FLAGS)
480 _SnapUpdateEwinFlags(sn, ewin);
481 #if USE_COMPOSITE
482 if (flags & SNAP_USE_OPACITY)
483 _SnapUpdateEwinOpacity(sn, ewin);
484 if (flags & SNAP_USE_SHADOW)
485 _SnapUpdateEwinShadow(sn, ewin);
486 #endif
487 if (flags & SNAP_USE_GROUPS)
488 _SnapUpdateEwinGroups(sn, ewin, ewin->num_groups);
489
490 SnapshotsSave();
491 }
492
493 static void
_EwinSnapSet(EWin * ewin,unsigned int match_flags,unsigned int use_flags)494 _EwinSnapSet(EWin * ewin, unsigned int match_flags, unsigned int use_flags)
495 {
496 Snapshot *sn;
497
498 /* Quit if nothing to be saved */
499 if (!match_flags || !(use_flags & SNAP_USE_ALL))
500 return;
501
502 sn = _SnapEwinGet(ewin, match_flags);
503 if (!sn)
504 return;
505
506 if (use_flags & SNAP_AUTO)
507 sn->track_changes = 1;
508
509 sn->use_flags = use_flags & SNAP_USE_ALL;
510
511 _SnapUpdateEwin(sn, ewin, use_flags);
512 }
513
514 void
SnapshotEwinUpdate(const EWin * ewin,unsigned int flags)515 SnapshotEwinUpdate(const EWin * ewin, unsigned int flags)
516 {
517 Snapshot *sn;
518
519 sn = ewin->snap;
520 if (!sn || !sn->track_changes)
521 return;
522
523 #if 0
524 Eprintf("%s: %s: %#x\n", __func__, EwinGetTitle(ewin), flags);
525 #endif
526
527 if (flags & sn->use_flags)
528 _SnapUpdateEwin(sn, ewin, flags);
529 }
530
531 /* unsnapshot any saved info about this ewin */
532 static void
_EwinSnapRemove(EWin * ewin)533 _EwinSnapRemove(EWin * ewin)
534 {
535 if (ewin->snap)
536 _SnapDestroy(ewin->snap);
537 ewin->snap = NULL;
538 }
539
540 #if ENABLE_DIALOGS
541 /*
542 * Snapshot dialogs
543 */
544 typedef struct {
545 EX_Window client;
546
547 struct {
548 char title;
549 char name;
550 char clss;
551 char role;
552 } match;
553
554 char track_changes;
555 char snap_border;
556 char snap_desktop;
557 char snap_size;
558 char snap_location;
559 char snap_layer;
560 char snap_sticky;
561 char snap_shaded;
562 char snap_cmd;
563 char snap_group;
564 char snap_skiplists;
565 char snap_flags;
566
567 #if USE_COMPOSITE
568 char snap_opacity;
569 char snap_shadow;
570 #endif
571 } SnapDlgData;
572
573 static void
_DlgApplySnap(Dialog * d,int val __UNUSED__,void * data __UNUSED__)574 _DlgApplySnap(Dialog * d, int val __UNUSED__, void *data __UNUSED__)
575 {
576 EWin *ewin;
577 SnapDlgData *sd = DLG_DATA_GET(d, SnapDlgData);
578 unsigned int match_flags, use_flags;
579
580 ewin = EwinFindByClient(sd->client);
581 if (!ewin)
582 goto done;
583
584 _EwinSnapRemove(ewin);
585
586 match_flags = 0;
587 if (sd->match.title)
588 match_flags |= SNAP_MATCH_TITLE;
589 if (sd->match.name)
590 match_flags |= SNAP_MATCH_NAME;
591 if (sd->match.clss)
592 match_flags |= SNAP_MATCH_CLASS;
593 if (sd->match.role)
594 match_flags |= SNAP_MATCH_ROLE;
595
596 if (!match_flags)
597 goto done;
598
599 use_flags = 0;
600 if (sd->track_changes)
601 use_flags |= SNAP_AUTO;
602 if (sd->snap_border)
603 use_flags |= SNAP_USE_BORDER;
604 if (sd->snap_cmd)
605 use_flags |= SNAP_USE_COMMAND;
606 if (sd->snap_desktop)
607 use_flags |= SNAP_USE_DESK;
608 if (sd->snap_location)
609 use_flags |= SNAP_USE_POS;
610 if (sd->snap_size)
611 use_flags |= SNAP_USE_SIZE;
612 if (sd->snap_layer)
613 use_flags |= SNAP_USE_LAYER;
614 if (sd->snap_sticky)
615 use_flags |= SNAP_USE_STICKY;
616 if (sd->snap_shaded)
617 use_flags |= SNAP_USE_SHADED;
618 if (sd->snap_skiplists)
619 use_flags |= SNAP_USE_SKIP_LISTS;
620 if (sd->snap_flags)
621 use_flags |= SNAP_USE_FLAGS;
622 #if USE_COMPOSITE
623 if (sd->snap_opacity)
624 use_flags |= SNAP_USE_OPACITY;
625 if (sd->snap_shadow)
626 use_flags |= SNAP_USE_SHADOW;
627 #endif
628 if (sd->snap_group)
629 use_flags |= SNAP_USE_GROUPS;
630
631 if (!use_flags)
632 goto done;
633
634 _EwinSnapSet(ewin, match_flags, use_flags);
635
636 done:
637 SnapshotsSave();
638 }
639
640 static void
_DlgFillSnap(Dialog * d,DItem * table,void * data)641 _DlgFillSnap(Dialog * d, DItem * table, void *data)
642 {
643 SnapDlgData *sd = DLG_DATA_GET(d, SnapDlgData);
644 DItem *di;
645 Snapshot *sn;
646 char s[1024];
647 const EWin *ewin = (EWin *) data;
648
649 sd->client = EwinGetClientXwin(ewin);
650
651 sn = ewin->snap;
652 if (sn)
653 {
654 sd->match.title = (sn->match_flags & SNAP_MATCH_TITLE) != 0;
655 sd->match.name = (sn->match_flags & SNAP_MATCH_NAME) != 0;
656 sd->match.clss = (sn->match_flags & SNAP_MATCH_CLASS) != 0;
657 sd->match.role = (sn->match_flags & SNAP_MATCH_ROLE) != 0;
658
659 if (sn->track_changes)
660 sd->track_changes = 1;
661 if (sn->use_flags & SNAP_USE_BORDER)
662 sd->snap_border = 1;
663 if (sn->use_flags & SNAP_USE_COMMAND)
664 sd->snap_cmd = 1;
665 if (sn->use_flags & SNAP_USE_DESK)
666 sd->snap_desktop = 1;
667 if (sn->use_flags & SNAP_USE_POS)
668 sd->snap_location = 1;
669 if (sn->use_flags & SNAP_USE_SIZE)
670 sd->snap_size = 1;
671 if (sn->use_flags & SNAP_USE_LAYER)
672 sd->snap_layer = 1;
673 if (sn->use_flags & SNAP_USE_STICKY)
674 sd->snap_sticky = 1;
675 if (sn->use_flags & SNAP_USE_SHADED)
676 sd->snap_shaded = 1;
677 if (sn->use_flags & SNAP_USE_SKIP_LISTS)
678 sd->snap_skiplists = 1;
679 if (sn->use_flags & SNAP_USE_FLAGS)
680 sd->snap_flags = 1;
681 #if USE_COMPOSITE
682 if (sn->use_flags & SNAP_USE_OPACITY)
683 sd->snap_opacity = 1;
684 if (sn->use_flags & SNAP_USE_SHADOW)
685 sd->snap_shadow = 1;
686 #endif
687 if (sn->use_flags & SNAP_USE_GROUPS)
688 sd->snap_group = 1;
689 }
690 else
691 {
692 if (EwinGetIcccmCName(ewin))
693 {
694 sd->match.name = 1;
695 sd->match.clss = 1;
696 sd->match.role = !!ewin->icccm.wm_role;
697 }
698 else
699 {
700 sd->match.title = !!EwinGetIcccmName(ewin);
701 }
702 }
703
704 table = DialogAddItem(table, DITEM_TABLE);
705 DialogItemTableSetOptions(table, 4, 0, 0, 0);
706
707 di = DialogAddItem(table, DITEM_CHECKBUTTON);
708 DialogItemSetAlign(di, 0, 512);
709 DialogItemSetText(di, _("Title:"));
710 DialogItemCheckButtonSetPtr(di, &sd->match.title);
711
712 di = DialogAddItem(table, DITEM_TEXT);
713 DialogItemSetColSpan(di, 3);
714 DialogItemSetAlign(di, 1024, 512);
715 DialogItemSetText(di, EwinGetIcccmName(ewin));
716
717 if (EwinGetIcccmCName(ewin))
718 {
719 di = DialogAddItem(table, DITEM_CHECKBUTTON);
720 DialogItemSetAlign(di, 0, 512);
721 DialogItemSetText(di, _("Name:"));
722 DialogItemCheckButtonSetPtr(di, &sd->match.name);
723
724 di = DialogAddItem(table, DITEM_TEXT);
725 DialogItemSetColSpan(di, 3);
726 DialogItemSetAlign(di, 1024, 512);
727 DialogItemSetText(di, EwinGetIcccmCName(ewin));
728 }
729
730 if (EwinGetIcccmClass(ewin))
731 {
732 di = DialogAddItem(table, DITEM_CHECKBUTTON);
733 DialogItemSetAlign(di, 0, 512);
734 DialogItemSetText(di, _("Class:"));
735 DialogItemCheckButtonSetPtr(di, &sd->match.clss);
736
737 di = DialogAddItem(table, DITEM_TEXT);
738 DialogItemSetColSpan(di, 3);
739 DialogItemSetAlign(di, 1024, 512);
740 DialogItemSetText(di, EwinGetIcccmClass(ewin));
741 }
742
743 if (ewin->icccm.wm_role)
744 {
745 di = DialogAddItem(table, DITEM_CHECKBUTTON);
746 DialogItemSetAlign(di, 0, 512);
747 DialogItemSetText(di, _("Role:"));
748 DialogItemCheckButtonSetPtr(di, &sd->match.role);
749
750 di = DialogAddItem(table, DITEM_TEXT);
751 DialogItemSetColSpan(di, 3);
752 DialogItemSetAlign(di, 1024, 512);
753 DialogItemSetText(di, ewin->icccm.wm_role);
754 }
755
756 if (ewin->icccm.wm_command)
757 {
758 di = DialogAddItem(table, DITEM_TEXT);
759 DialogItemSetAlign(di, 0, 512);
760 DialogItemSetText(di, _("Command:"));
761
762 di = DialogAddItem(table, DITEM_TEXT);
763 DialogItemSetColSpan(di, 3);
764 DialogItemSetAlign(di, 1024, 512);
765
766 /* if the command is long, cut in into slices of about 80 characters */
767 if (strlen(ewin->icccm.wm_command) > 80)
768 {
769 int i = 0, slice, last;
770
771 s[0] = 0;
772 while ((i <= (int)strlen(ewin->icccm.wm_command))
773 && (i < (int)(sizeof(s) / 4)))
774 {
775 last = i;
776 i += 64;
777 slice = 64;
778 /* and make sure that we don't cut in the middle of a word. */
779 while ((ewin->icccm.wm_command[i++] != ' ')
780 && (i < (int)(sizeof(s) / 4)))
781 slice++;
782 strncat(s, ewin->icccm.wm_command + last, slice);
783 if (i < (int)(sizeof(s) / 4))
784 strcat(s, "\n");
785 else
786 strcat(s, "...\n");
787 }
788 DialogItemSetText(di, s);
789 }
790 else
791 DialogItemSetText(di, ewin->icccm.wm_command);
792 }
793
794 di = DialogAddItem(table, DITEM_SEPARATOR);
795 DialogItemSetColSpan(di, 4);
796
797 di = DialogAddItem(table, DITEM_CHECKBUTTON);
798 DialogItemSetColSpan(di, 4);
799 DialogItemSetText(di, _("Track Changes"));
800 DialogItemCheckButtonSetPtr(di, &sd->track_changes);
801
802 di = DialogAddItem(table, DITEM_CHECKBUTTON);
803 DialogItemSetColSpan(di, 2);
804 DialogItemSetText(di, _("Location"));
805 DialogItemCheckButtonSetPtr(di, &sd->snap_location);
806
807 di = DialogAddItem(table, DITEM_CHECKBUTTON);
808 DialogItemSetColSpan(di, 2);
809 DialogItemSetText(di, _("Border style"));
810 DialogItemCheckButtonSetPtr(di, &sd->snap_border);
811
812 di = DialogAddItem(table, DITEM_CHECKBUTTON);
813 DialogItemSetColSpan(di, 2);
814 DialogItemSetText(di, _("Size"));
815 DialogItemCheckButtonSetPtr(di, &sd->snap_size);
816
817 di = DialogAddItem(table, DITEM_CHECKBUTTON);
818 DialogItemSetColSpan(di, 2);
819 DialogItemSetText(di, _("Desktop"));
820 DialogItemCheckButtonSetPtr(di, &sd->snap_desktop);
821
822 di = DialogAddItem(table, DITEM_CHECKBUTTON);
823 DialogItemSetColSpan(di, 2);
824 DialogItemSetText(di, _("Shaded state"));
825 DialogItemCheckButtonSetPtr(di, &sd->snap_shaded);
826
827 di = DialogAddItem(table, DITEM_CHECKBUTTON);
828 DialogItemSetColSpan(di, 2);
829 DialogItemSetText(di, _("Sticky state"));
830 DialogItemCheckButtonSetPtr(di, &sd->snap_sticky);
831
832 di = DialogAddItem(table, DITEM_CHECKBUTTON);
833 DialogItemSetColSpan(di, 2);
834 DialogItemSetText(di, _("Stacking layer"));
835 DialogItemCheckButtonSetPtr(di, &sd->snap_layer);
836
837 di = DialogAddItem(table, DITEM_CHECKBUTTON);
838 DialogItemSetColSpan(di, 2);
839 DialogItemSetText(di, _("Window List Skip"));
840 DialogItemCheckButtonSetPtr(di, &sd->snap_skiplists);
841
842 #if USE_COMPOSITE
843 di = DialogAddItem(table, DITEM_CHECKBUTTON);
844 DialogItemSetColSpan(di, 2);
845 DialogItemSetText(di, _("Opacity"));
846 DialogItemCheckButtonSetPtr(di, &sd->snap_opacity);
847
848 di = DialogAddItem(table, DITEM_CHECKBUTTON);
849 DialogItemSetColSpan(di, 2);
850 DialogItemSetText(di, _("Shadowing"));
851 DialogItemCheckButtonSetPtr(di, &sd->snap_shadow);
852 #endif
853
854 di = DialogAddItem(table, DITEM_CHECKBUTTON);
855 DialogItemSetColSpan(di, 2);
856 DialogItemSetText(di, _("Flags"));
857 DialogItemCheckButtonSetPtr(di, &sd->snap_flags);
858
859 di = DialogAddItem(table, DITEM_NONE);
860 DialogItemSetColSpan(di, 2);
861
862 if (ewin->icccm.wm_command)
863 {
864 char ok = 1;
865
866 if (ewin->icccm.wm_machine)
867 {
868 if (strcmp(ewin->icccm.wm_machine, Mode.wm.machine_name))
869 ok = 0;
870 }
871 if (ok)
872 {
873 di = DialogAddItem(table, DITEM_CHECKBUTTON);
874 DialogItemSetColSpan(di, 4);
875 DialogItemSetText(di, _("Restart application on login"));
876 DialogItemCheckButtonSetPtr(di, &sd->snap_cmd);
877 }
878 else
879 {
880 di = DialogAddItem(table, DITEM_NONE);
881 DialogItemSetColSpan(di, 4);
882 }
883 }
884 else
885 {
886 di = DialogAddItem(table, DITEM_NONE);
887 DialogItemSetColSpan(di, 4);
888 }
889
890 if (ewin->groups)
891 {
892 di = DialogAddItem(table, DITEM_CHECKBUTTON);
893 DialogItemSetColSpan(di, 4);
894 DialogItemSetText(di, _("Remember this window's group(s)"));
895 DialogItemCheckButtonSetPtr(di, &sd->snap_group);
896 }
897 }
898
899 static const DialogDef DlgSnap = {
900 NULL,
901 NULL, N_("Remembered Application Attributes"),
902 sizeof(SnapDlgData),
903 SOUND_NONE,
904 "pix/snapshots.png",
905 N_("Select the attributes of this\n"
906 "window you wish to Remember\n" "from now on\n"),
907 _DlgFillSnap,
908 DLG_OAC, _DlgApplySnap, NULL
909 };
910
911 static void
_EwinSnapDialog(const EWin * ewin)912 _EwinSnapDialog(const EWin * ewin)
913 {
914 char s[1024];
915
916 Esnprintf(s, sizeof(s), "SNAPSHOT_WINDOW-%#x", EwinGetClientXwin(ewin));
917
918 DialogShowSimpleWithName(&DlgSnap, s, (void *)ewin);
919 }
920
921 /* list of remembered items for the remember dialog -- it's either
922 * _another_ global var, or a wrapper struct to pass data to the
923 * callback funcs besides the dialog itself -- this is much easier */
924
925 typedef struct _remwinlist {
926 Snapshot *snap;
927 char remove;
928 } RememberWinList;
929
930 static RememberWinList *rd_ewin_list;
931
932 static void
_DlgApplyRemember(Dialog * d __UNUSED__,int val __UNUSED__,void * data __UNUSED__)933 _DlgApplyRemember(Dialog * d __UNUSED__,
934 int val __UNUSED__, void *data __UNUSED__)
935 {
936 int i;
937
938 if (!rd_ewin_list)
939 return;
940
941 for (i = 0; rd_ewin_list[i].snap; i++)
942 {
943 if (!rd_ewin_list[i].remove)
944 continue;
945
946 _SnapDestroy(rd_ewin_list[i].snap);
947 }
948 /* save snapshot info to disk */
949 SnapshotsSave();
950 }
951
952 static void
_DlgExitRemember(Dialog * d __UNUSED__)953 _DlgExitRemember(Dialog * d __UNUSED__)
954 {
955 EFREE_NULL(rd_ewin_list);
956 }
957
958 static void
CB_RememberWindowSettings(Dialog * d __UNUSED__,int val __UNUSED__,void * data)959 CB_RememberWindowSettings(Dialog * d __UNUSED__, int val __UNUSED__, void *data)
960 {
961 RememberWinList *rd = (RememberWinList *) data;
962 Snapshot *sn;
963
964 if (!rd)
965 return;
966
967 /* Make sure its still there */
968 sn = LIST_CHECK(Snapshot, &ss_list, rd->snap);
969
970 if (!sn || !sn->used)
971 return;
972 _EwinSnapDialog(sn->used);
973 }
974
975 static void
_DlgFillRemember(Dialog * d __UNUSED__,DItem * table,void * data __UNUSED__)976 _DlgFillRemember(Dialog * d __UNUSED__, DItem * table, void *data __UNUSED__)
977 {
978 DItem *di;
979 Snapshot *sn;
980 int i, num;
981 char buf[128];
982 const char *s;
983
984 DialogItemTableSetOptions(table, 3, 0, 0, 0);
985
986 num = LIST_GET_COUNT(&ss_list);
987 rd_ewin_list = EMALLOC(RememberWinList, num + 1);
988
989 if (num > 0)
990 {
991 di = DialogAddItem(table, DITEM_TEXT);
992 DialogItemSetColSpan(di, 3);
993 DialogItemSetFill(di, 0, 0);
994 DialogItemSetAlign(di, 0, 512);
995 DialogItemSetText(di, _("Delete"));
996 }
997
998 i = 0;
999 LIST_FOR_EACH(Snapshot, &ss_list, sn)
1000 {
1001 rd_ewin_list[i].snap = sn;
1002 rd_ewin_list[i].remove = 0;
1003
1004 di = DialogAddItem(table, DITEM_CHECKBUTTON);
1005 DialogItemSetColSpan(di, 2);
1006 DialogItemSetAlign(di, 0, 512);
1007 if (sn->used)
1008 s = EwinGetTitle(sn->used);
1009 else if (sn->win_title)
1010 s = sn->win_title;
1011 else
1012 {
1013 Esnprintf(buf, sizeof(buf), "%s.%s", sn->win_name, sn->win_class);
1014 s = buf;
1015 }
1016 DialogItemSetText(di, s);
1017 DialogItemCheckButtonSetPtr(di, &(rd_ewin_list[i].remove));
1018
1019 if (sn->used)
1020 {
1021 di = DialogAddItem(table, DITEM_BUTTON);
1022 DialogItemSetAlign(di, 0, 512);
1023 DialogItemSetText(di, _("Remembered Settings..."));
1024 DialogItemSetCallback(di, CB_RememberWindowSettings, 0,
1025 (char *)(&rd_ewin_list[i]));
1026 }
1027 else
1028 {
1029 di = DialogAddItem(table, DITEM_TEXT);
1030 DialogItemSetText(di, _("Unused"));
1031 }
1032 i++;
1033 }
1034 rd_ewin_list[num].snap = NULL;
1035
1036 /* finish remember window */
1037 if (!num)
1038 {
1039 di = DialogAddItem(table, DITEM_TEXT);
1040 DialogItemSetColSpan(di, 3);
1041 DialogItemSetText(di,
1042 _
1043 ("There are no active windows with remembered attributes."));
1044 }
1045 }
1046
1047 const DialogDef DlgRemember = {
1048 "CONFIGURE_PAGER",
1049 N_("Remember"), N_("Remembered Windows Settings"),
1050 0,
1051 SOUND_SETTINGS_PAGER,
1052 "pix/snapshots.png",
1053 N_("Enlightenment Remembered\n" "Windows Settings Dialog"),
1054 _DlgFillRemember,
1055 DLG_OC, _DlgApplyRemember, _DlgExitRemember
1056 };
1057 #endif /* ENABLE_DIALOGS */
1058
1059 /* ... combine writes, only save after a timeout */
1060 static int
_SnapshotsSaveReal(void * data __UNUSED__)1061 _SnapshotsSaveReal(void *data __UNUSED__)
1062 {
1063 SnapshotsSaveReal();
1064 return 0;
1065 }
1066
1067 void
SnapshotsSave(void)1068 SnapshotsSave(void)
1069 {
1070 TIMER_DEL(ss_timer);
1071 TIMER_ADD(ss_timer, 5000, _SnapshotsSaveReal, NULL);
1072 }
1073
1074 /* save out all snapped info to disk */
1075 void
SnapshotsSaveReal(void)1076 SnapshotsSaveReal(void)
1077 {
1078 Snapshot *sn;
1079 int j;
1080 char buf[4096], s[4096];
1081 FILE *f;
1082
1083 if (!Mode.wm.save_ok)
1084 goto done;
1085
1086 Etmp(s);
1087 f = fopen(s, "w");
1088 if (!f)
1089 goto done;
1090
1091 LIST_FOR_EACH(Snapshot, &ss_list, sn)
1092 {
1093 fprintf(f, "NEW: %s\n", sn->name);
1094 if (sn->used)
1095 fprintf(f, "WIN: %#x\n", EwinGetClientXwin(sn->used));
1096 if ((sn->match_flags & SNAP_MATCH_TITLE) && sn->win_title)
1097 fprintf(f, "TITLE: %s\n", sn->win_title);
1098 if ((sn->match_flags & SNAP_MATCH_NAME) && sn->win_name)
1099 fprintf(f, "NAME: %s\n", sn->win_name);
1100 if ((sn->match_flags & SNAP_MATCH_CLASS) && sn->win_class)
1101 fprintf(f, "CLASS: %s\n", sn->win_class);
1102 if ((sn->match_flags & SNAP_MATCH_ROLE) && sn->win_role)
1103 fprintf(f, "ROLE: %s\n", sn->win_role);
1104 if (sn->track_changes)
1105 fprintf(f, "AUTO: yes\n");
1106 if ((sn->use_flags & SNAP_USE_BORDER) && sn->border_name)
1107 fprintf(f, "BORDER: %s\n", sn->border_name);
1108 if ((sn->use_flags & SNAP_USE_COMMAND) && sn->cmd)
1109 fprintf(f, "CMD: %s\n", sn->cmd);
1110 if (sn->use_flags & SNAP_USE_DESK)
1111 fprintf(f, "DESKTOP: %i\n", sn->desktop);
1112 if (sn->use_flags & SNAP_USE_POS)
1113 fprintf(f, "RES: %i %i\n", WinGetW(VROOT), WinGetH(VROOT));
1114 if (sn->use_flags & SNAP_USE_SIZE)
1115 fprintf(f, "WH: %i %i\n", sn->w, sn->h);
1116 if (sn->use_flags & SNAP_USE_POS)
1117 fprintf(f, "XY: %i %i %i %i\n", sn->x, sn->y, sn->area_x, sn->area_y);
1118 if (sn->use_flags & SNAP_USE_LAYER)
1119 fprintf(f, "LAYER: %i\n", sn->layer);
1120 if (sn->use_flags & SNAP_USE_STICKY)
1121 fprintf(f, "STICKY: %i\n", sn->sticky);
1122 if (sn->use_flags & SNAP_USE_SHADED)
1123 fprintf(f, "SHADE: %i\n", sn->shaded);
1124 if (sn->use_flags & SNAP_USE_SKIP_LISTS)
1125 {
1126 fprintf(f, "SKIPTASK: %i\n", sn->skiptask);
1127 fprintf(f, "SKIPWINLIST: %i\n", sn->skipwinlist);
1128 fprintf(f, "SKIPFOCUS: %i\n", sn->skipfocus);
1129 }
1130 if (sn->use_flags & SNAP_USE_FLAGS)
1131 fprintf(f, "FLAGS: %#x %#x\n", sn->flags[0], sn->flags[1]);
1132 #if USE_COMPOSITE
1133 if (sn->use_flags & SNAP_USE_OPACITY)
1134 fprintf(f, "OPACITY: %i %i\n", sn->opacity, sn->focused_opacity);
1135 if (sn->use_flags & SNAP_USE_SHADOW)
1136 fprintf(f, "SHADOW: %i\n", sn->shadow);
1137 #endif
1138 if (sn->use_flags & SNAP_USE_GROUPS)
1139 {
1140 fprintf(f, "GROUP:");
1141 for (j = 0; j < sn->num_groups; j++)
1142 fprintf(f, " %i", sn->groups[j]);
1143 fprintf(f, "\n");
1144 }
1145 fprintf(f, "\n");
1146 }
1147
1148 fclose(f);
1149
1150 Esnprintf(buf, sizeof(buf), "%s.snapshots", EGetSavePrefix());
1151
1152 if (EDebug(EDBUG_TYPE_SNAPS))
1153 Eprintf("%s: %s\n", __func__, buf);
1154 E_mv(s, buf);
1155 if (!isfile(buf))
1156 Alert(_("Error saving snaps file"));
1157
1158 done:
1159 TIMER_DEL(ss_timer);
1160 }
1161
1162 void
SnapshotsSpawn(void)1163 SnapshotsSpawn(void)
1164 {
1165 Snapshot *sn;
1166
1167 LIST_FOR_EACH(Snapshot, &ss_list, sn)
1168 {
1169 if ((sn->use_flags & SNAP_USE_COMMAND) && (sn->cmd) &&
1170 !sn->used && !(sn->match_flags & SNAP_MATCH_MULTIPLE))
1171 {
1172 sn->startup_id = ++Mode.apps.startup_id;
1173 Espawn("%s", sn->cmd);
1174 }
1175 }
1176 }
1177
1178 /* load all snapped info */
1179 static int
_SnapshotsLoad(FILE * fs)1180 _SnapshotsLoad(FILE * fs)
1181 {
1182 Snapshot *sn = NULL;
1183 char buf[4096], *s;
1184 int res_w, res_h, a, b, c, d;
1185
1186 res_w = WinGetW(VROOT);
1187 res_h = WinGetH(VROOT);
1188 while (fgets(buf, sizeof(buf), fs))
1189 {
1190 s = strchr(buf, ':');
1191 if (!s)
1192 continue;
1193 *s++ = '\0';
1194 s = Estrtrim(s);
1195 if (!buf[0])
1196 continue;
1197 if (!strcmp(buf, "NEW"))
1198 {
1199 res_w = WinGetW(VROOT);
1200 res_h = WinGetH(VROOT);
1201 sn = _SnapCreate(s);
1202 }
1203 else if (sn)
1204 {
1205 if (!strcmp(buf, "WIN"))
1206 {
1207 sn->win = strtoul(s, NULL, 0);
1208 }
1209 else if (!strcmp(buf, "TITLE"))
1210 {
1211 sn->win_title = Estrdup(s);
1212 sn->match_flags |= SNAP_MATCH_TITLE;
1213 }
1214 else if (!strcmp(buf, "NAME"))
1215 {
1216 sn->win_name = Estrdup(s);
1217 sn->match_flags |= SNAP_MATCH_NAME;
1218 }
1219 else if (!strcmp(buf, "CLASS"))
1220 {
1221 sn->win_class = Estrdup(s);
1222 sn->match_flags |= SNAP_MATCH_CLASS;
1223 }
1224 else if (!strcmp(buf, "ROLE"))
1225 {
1226 sn->win_role = Estrdup(s);
1227 sn->match_flags |= SNAP_MATCH_ROLE;
1228 }
1229 else if (!strcmp(buf, "AUTO"))
1230 {
1231 sn->track_changes = 1;
1232 }
1233 else if (!strcmp(buf, "BORDER"))
1234 {
1235 sn->use_flags |= SNAP_USE_BORDER;
1236 sn->border_name = Estrdup(s);
1237 }
1238 else if (!strcmp(buf, "CMD"))
1239 {
1240 sn->use_flags |= SNAP_USE_COMMAND;
1241 sn->cmd = Estrdup(s);
1242 }
1243 else if (!strcmp(buf, "DESKTOP"))
1244 {
1245 sn->use_flags |= SNAP_USE_DESK;
1246 sn->desktop = atoi(s);
1247 }
1248 else if (!strcmp(buf, "RES"))
1249 {
1250 if (sscanf(s, "%u %u", &a, &b) < 2)
1251 continue;
1252 if (a <= 0 || b <= 0)
1253 continue;
1254 res_w = a;
1255 res_h = b;
1256 }
1257 else if (!strcmp(buf, "WH"))
1258 {
1259 if (sscanf(s, "%u %u", &a, &b) < 2)
1260 continue;
1261 if (a <= 0 || b <= 0)
1262 continue;
1263 sn->use_flags |= SNAP_USE_SIZE;
1264 sn->w = a;
1265 sn->h = b;
1266 }
1267 else if (!strcmp(buf, "XY"))
1268 {
1269 if (sscanf(s, "%d %d %u %u", &a, &b, &c, &d) < 4)
1270 continue;
1271 sn->use_flags |= SNAP_USE_POS;
1272 sn->x = a;
1273 sn->y = b;
1274 /* we changed reses since we last used this snapshot file */
1275 if (res_w != WinGetW(VROOT))
1276 {
1277 if (sn->use_flags & SNAP_USE_SIZE)
1278 {
1279 if ((res_w - sn->w) <= 0)
1280 sn->x = 0;
1281 else
1282 sn->x =
1283 (sn->x * (WinGetW(VROOT) - sn->w)) /
1284 (res_w - sn->w);
1285 }
1286 else
1287 {
1288 if (sn->x >= WinGetW(VROOT))
1289 sn->x = WinGetW(VROOT) - 32;
1290 }
1291 }
1292 if (res_h != WinGetH(VROOT))
1293 {
1294 if (sn->use_flags & SNAP_USE_SIZE)
1295 {
1296 if ((res_h - sn->h) <= 0)
1297 sn->y = 0;
1298 else
1299 sn->y =
1300 (sn->y * (WinGetH(VROOT) - sn->h)) /
1301 (res_h - sn->h);
1302 }
1303 else
1304 {
1305 if (sn->y >= WinGetH(VROOT))
1306 sn->y = WinGetH(VROOT) - 32;
1307 }
1308 }
1309 sn->area_x = c;
1310 sn->area_y = d;
1311 }
1312 else if (!strcmp(buf, "LAYER"))
1313 {
1314 sn->use_flags |= SNAP_USE_LAYER;
1315 sn->layer = atoi(s);
1316 }
1317 else if (!strcmp(buf, "STICKY"))
1318 {
1319 sn->use_flags |= SNAP_USE_STICKY;
1320 sn->sticky = atoi(s);
1321 }
1322 else if (!strcmp(buf, "SHADE"))
1323 {
1324 sn->use_flags |= SNAP_USE_SHADED;
1325 sn->shaded = atoi(s);
1326 }
1327 else if (!strcmp(buf, "SKIPFOCUS"))
1328 {
1329 sn->use_flags |= SNAP_USE_SKIP_LISTS;
1330 sn->skipfocus = atoi(s);
1331 }
1332 else if (!strcmp(buf, "SKIPTASK"))
1333 {
1334 sn->use_flags |= SNAP_USE_SKIP_LISTS;
1335 sn->skiptask = atoi(s);
1336 }
1337 else if (!strcmp(buf, "SKIPWINLIST"))
1338 {
1339 sn->use_flags |= SNAP_USE_SKIP_LISTS;
1340 sn->skipwinlist = atoi(s);
1341 }
1342 else if (!strcmp(buf, "FLAGS"))
1343 {
1344 sn->use_flags |= SNAP_USE_FLAGS;
1345 sn->flags[0] = sn->flags[1] = 0;
1346 sscanf(s, "%i %i", sn->flags, sn->flags + 1);
1347 }
1348 else if (!strcmp(buf, "GROUP"))
1349 {
1350 sn->use_flags |= SNAP_USE_GROUPS;
1351 for (;; s += b)
1352 {
1353 b = 0;
1354 sscanf(s, "%d %n", &a, &b);
1355 if (b <= 0)
1356 break;
1357 sn->num_groups++;
1358 sn->groups = EREALLOC(int, sn->groups, sn->num_groups);
1359 sn->groups[sn->num_groups - 1] = a;
1360 GroupSetUsed(a);
1361 }
1362 }
1363 #if USE_COMPOSITE
1364 else if (!strcmp(buf, "OPACITY"))
1365 {
1366 sn->use_flags |= SNAP_USE_OPACITY;
1367 a = 100;
1368 b = 100;
1369 sscanf(s, "%i %i", &a, &b);
1370 if (b == 1)
1371 b = 100; /* BW compat - focused is opaque */
1372 sn->opacity = a;
1373 sn->focused_opacity = b;
1374 }
1375 else if (!strcmp(buf, "SHADOW"))
1376 {
1377 sn->use_flags |= SNAP_USE_SHADOW;
1378 sn->shadow = atoi(s);
1379 }
1380 #endif
1381 }
1382 }
1383
1384 return 0;
1385 }
1386
1387 void
SnapshotsLoad(void)1388 SnapshotsLoad(void)
1389 {
1390 char s[4096];
1391
1392 GroupsLoad();
1393
1394 Esnprintf(s, sizeof(s), "%s.snapshots", EGetSavePrefix());
1395
1396 ConfigFileLoad(s, NULL, _SnapshotsLoad, 0);
1397
1398 /* Remove groups not referenced by snapshots */
1399 GroupsPrune();
1400 }
1401
1402 /* make a client window conform to snapshot info */
1403 void
SnapshotEwinApply(EWin * ewin)1404 SnapshotEwinApply(EWin * ewin)
1405 {
1406 Snapshot *sn;
1407 int ax, ay;
1408 unsigned int use_flags;
1409
1410 _SnapEwinFind(ewin); /* Find a saved settings match */
1411
1412 sn = ewin->snap;
1413 if (!sn)
1414 {
1415 if (ewin->props.autosave)
1416 _EwinSnapSet(ewin, SNAP_MATCH_DEFAULT, SNAP_USE_ALL | SNAP_AUTO);
1417 return;
1418 }
1419
1420 if (ewin->props.autosave)
1421 sn->track_changes = 1;
1422
1423 use_flags = sn->use_flags;
1424 /* If restarting don't override stuff set in attributes/properties */
1425 if (ewin->state.identified)
1426 use_flags &= SNAP_USE_LAYER | SNAP_USE_SHADOW | SNAP_USE_GROUPS |
1427 SNAP_USE_OPACITY;
1428
1429 if (use_flags & SNAP_USE_STICKY)
1430 EoSetSticky(ewin, sn->sticky);
1431
1432 if (use_flags & SNAP_USE_DESK)
1433 EoSetDesk(ewin, DeskGetValid(sn->desktop));
1434
1435 if (use_flags & SNAP_USE_SIZE)
1436 {
1437 ewin->client.w = sn->w;
1438 ewin->client.h = sn->h;
1439 ewin->state.maximized_horz = ewin->state.maximized_vert = 0;
1440 }
1441
1442 if (use_flags & SNAP_USE_POS)
1443 {
1444 ewin->state.placed = 1;
1445 ewin->client.x = sn->x;
1446 ewin->client.y = sn->y;
1447 ewin->icccm.grav = NorthWestGravity;
1448 #if 0 /* No, do later in EwinDetermineArea() */
1449 ewin->area_x = sn->area_x;
1450 ewin->area_y = sn->area_y;
1451 #endif
1452 if (!EoIsSticky(ewin))
1453 {
1454 DeskGetArea(EoGetDesk(ewin), &ax, &ay);
1455 ewin->client.x += ((sn->area_x - ax) * WinGetW(VROOT));
1456 ewin->client.y += ((sn->area_y - ay) * WinGetH(VROOT));
1457 }
1458 }
1459
1460 if (use_flags & SNAP_USE_LAYER)
1461 EoSetLayer(ewin, sn->layer);
1462
1463 if (use_flags & SNAP_USE_SKIP_LISTS)
1464 {
1465 ewin->props.skip_focuslist = sn->skipfocus;
1466 ewin->props.skip_ext_task = sn->skiptask;
1467 ewin->props.skip_winlist = sn->skipwinlist;
1468 }
1469
1470 if (use_flags & SNAP_USE_FLAGS)
1471 EwinFlagsDecode(ewin, sn->flags);
1472
1473 if (use_flags & SNAP_USE_SHADED)
1474 ewin->state.shaded = sn->shaded;
1475
1476 if (use_flags & SNAP_USE_BORDER)
1477 EwinBorderSetInitially(ewin, sn->border_name);
1478
1479 if (use_flags & SNAP_USE_GROUPS)
1480 GroupsEwinAdd(ewin, sn->groups, sn->num_groups);
1481
1482 #if USE_COMPOSITE
1483 if (use_flags & SNAP_USE_OPACITY)
1484 {
1485 sn->opacity = OpacityFix(sn->opacity, 0);
1486 sn->focused_opacity = OpacityFix(sn->focused_opacity, 0);
1487 ewin->props.opacity = OpacityFromPercent(sn->opacity);
1488 ewin->props.focused_opacity = OpacityFromPercent(sn->focused_opacity);
1489 ewin->ewmh.opacity_update = 1; /* Set opacity on client window */
1490 }
1491
1492 if (use_flags & SNAP_USE_SHADOW)
1493 EoSetShadow(ewin, sn->shadow);
1494 #endif
1495
1496 if (EDebug(EDBUG_TYPE_SNAPS))
1497 Eprintf("Snap get snap %#x: %4d+%4d %4dx%4d: %s\n",
1498 EwinGetClientXwin(ewin), ewin->client.x, ewin->client.y,
1499 ewin->client.w, ewin->client.h, EwinGetTitle(ewin));
1500 }
1501
1502 /* Detach snapshot from ewin */
1503 void
SnapshotEwinUnmatch(EWin * ewin)1504 SnapshotEwinUnmatch(EWin * ewin)
1505 {
1506 Snapshot *sn;
1507
1508 sn = ewin->snap;
1509 if (!sn)
1510 return;
1511
1512 ewin->snap = NULL;
1513 sn->used = NULL;
1514 }
1515
1516 void
SnapshotEwinParse(EWin * ewin,const char * params)1517 SnapshotEwinParse(EWin * ewin, const char *params)
1518 {
1519 char param[1024];
1520 const char *p;
1521 int len;
1522 unsigned int match_flags, use_flags;
1523
1524 p = params;
1525 if (!p)
1526 return;
1527
1528 match_flags = SNAP_MATCH_DEFAULT;
1529 use_flags = 0;
1530
1531 for (;;)
1532 {
1533 param[0] = '\0';
1534 len = 0;
1535 sscanf(p, "%s %n", param, &len);
1536 if (len <= 0)
1537 break;
1538 p += len;
1539
1540 if (!strcmp(param, "all"))
1541 {
1542 use_flags = SNAP_USE_ALL;
1543 }
1544 #if ENABLE_DIALOGS
1545 else if (!strcmp(param, "dialog"))
1546 {
1547 _EwinSnapDialog(ewin);
1548 break;
1549 }
1550 #endif
1551 else if (!strcmp(param, "none"))
1552 _EwinSnapRemove(ewin);
1553 else if (!strcmp(param, "auto"))
1554 use_flags |= SNAP_AUTO;
1555 else if (!strcmp(param, "border"))
1556 use_flags |= SNAP_USE_BORDER;
1557 else if (!strcmp(param, "command"))
1558 use_flags |= SNAP_USE_COMMAND;
1559 else if (!strcmp(param, "desktop"))
1560 use_flags |= SNAP_USE_DESK;
1561 else if (!strcmp(param, "location"))
1562 use_flags |= SNAP_USE_POS;
1563 else if (!strcmp(param, "size"))
1564 use_flags |= SNAP_USE_SIZE;
1565 else if (!strcmp(param, "layer"))
1566 use_flags |= SNAP_USE_LAYER;
1567 else if (!strcmp(param, "shade"))
1568 use_flags |= SNAP_USE_SHADED;
1569 else if (!strcmp(param, "sticky"))
1570 use_flags |= SNAP_USE_STICKY;
1571 #if USE_COMPOSITE
1572 else if (!strcmp(param, "opacity"))
1573 use_flags |= SNAP_USE_OPACITY;
1574 else if (!strcmp(param, "shadow"))
1575 use_flags |= SNAP_USE_SHADOW;
1576 #endif
1577 else if (!strcmp(param, "group"))
1578 use_flags |= SNAP_USE_GROUPS;
1579 }
1580
1581 if (ewin->snap)
1582 {
1583 match_flags = ewin->snap->match_flags;
1584 use_flags |= ewin->snap->use_flags;
1585 }
1586
1587 _EwinSnapSet(ewin, match_flags, use_flags);
1588
1589 SnapshotsSave();
1590 }
1591
1592 /*
1593 * IPC functions
1594 * A bit ugly...
1595 */
1596 const char SnapshotsIpcText[] =
1597 "usage:\n" " list_remember [full]\n"
1598 " Retrieve a list of remembered windows. with full, the list\n"
1599 " includes the window's remembered attributes\n";
1600
1601 #define SS(s) ((s) ? (s) : NoText)
1602 static const char NoText[] = "-NONE-";
1603
1604 static void
_SnapShow(void * data,void * prm)1605 _SnapShow(void *data, void *prm)
1606 {
1607 Snapshot *sn = (Snapshot *) data;
1608 int full = !!prm;
1609 char buf[FILEPATH_LEN_MAX];
1610 const char *name;
1611
1612 name = (sn->name) ? sn->name : "???";
1613
1614 if (!full)
1615 {
1616 if (sn->used)
1617 IpcPrintf("%s\n", name);
1618 else
1619 IpcPrintf("%s (unused)\n", name);
1620 return;
1621 }
1622
1623 #define SU(sn, item) ((sn->match_flags & item) ? '>' : ':')
1624
1625 if (sn->used)
1626 Esnprintf(buf, sizeof(buf), "In use - %#x", EwinGetClientXwin(sn->used));
1627 else
1628 Esnprintf(buf, sizeof(buf), "*** Unused ***");
1629 IpcPrintf(" Snapshot Name: %s %s\n", name, buf);
1630 if (sn->win_title)
1631 IpcPrintf(" Window Title%c %s\n", SU(sn, SNAP_MATCH_TITLE),
1632 sn->win_title);
1633 if (sn->win_name)
1634 IpcPrintf(" Window Name%c %s\n", SU(sn, SNAP_MATCH_NAME),
1635 sn->win_name);
1636 if (sn->win_class)
1637 IpcPrintf(" Window Class%c %s\n", SU(sn, SNAP_MATCH_CLASS),
1638 sn->win_class);
1639 if (sn->win_role)
1640 IpcPrintf(" Window Role%c %s\n", SU(sn, SNAP_MATCH_ROLE),
1641 sn->win_role);
1642
1643 if (sn->track_changes)
1644 IpcPrintf(" Tracking changes\n");
1645 if (sn->use_flags & SNAP_USE_BORDER)
1646 IpcPrintf(" Border Name: %s\n", SS(sn->border_name));
1647 if (sn->use_flags & SNAP_USE_DESK)
1648 IpcPrintf(" desktop: %d\n", sn->desktop);
1649 if (sn->use_flags & SNAP_USE_POS)
1650 IpcPrintf(" (x, y): %d, %d area (x, y): %d, %d\n",
1651 sn->x, sn->y, sn->area_x, sn->area_y);
1652 if (sn->use_flags & SNAP_USE_SIZE)
1653 IpcPrintf(" (w, h): %d, %d\n", sn->w, sn->h);
1654 if (sn->use_flags & SNAP_USE_LAYER)
1655 IpcPrintf(" layer: %d\n", sn->layer);
1656 if (sn->use_flags & SNAP_USE_STICKY)
1657 IpcPrintf(" sticky: %d\n", sn->sticky);
1658 if (sn->use_flags & SNAP_USE_SHADED)
1659 IpcPrintf(" shade: %d\n", sn->shaded);
1660 if (sn->use_flags & SNAP_USE_COMMAND)
1661 IpcPrintf(" command: %s\n", SS(sn->cmd));
1662 if (sn->use_flags & SNAP_USE_SKIP_LISTS)
1663 IpcPrintf
1664 (" skiptask: %d skipfocus: %d skipwinlist: %d\n",
1665 sn->skiptask, sn->skipfocus, sn->skipwinlist);
1666 if (sn->use_flags & SNAP_USE_FLAGS)
1667 IpcPrintf(" flags: %#x %#x\n", sn->flags[0], sn->flags[1]);
1668 IpcPrintf("\n");
1669 }
1670
1671 void
SnapshotsIpcFunc(const char * params)1672 SnapshotsIpcFunc(const char *params)
1673 {
1674 const char *p;
1675 char cmd[128], prm[4096];
1676 int len;
1677 Snapshot *sn;
1678
1679 cmd[0] = prm[0] = '\0';
1680 p = params;
1681 if (p)
1682 {
1683 len = 0;
1684 sscanf(p, "%100s %4000s %n", cmd, prm, &len);
1685 p += len;
1686 }
1687
1688 if (LIST_IS_EMPTY(&ss_list))
1689 {
1690 IpcPrintf("No remembered windows\n");
1691 return;
1692 }
1693
1694 if (!p || cmd[0] == '?')
1695 {
1696 LIST_FOR_EACH(Snapshot, &ss_list, sn) _SnapShow(sn, NULL);
1697 }
1698 else
1699 {
1700 LIST_FOR_EACH(Snapshot, &ss_list, sn) _SnapShow(sn, (void *)1L);
1701 }
1702 }
1703