chiark / gitweb /
Imported Debian patch 1.0.0-5
[e16] / src / warp.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2009 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 /*
25  * Author: Merlin Hughes
26  *  - merlin@merlin.org
27  *
28  *  This code is free software.
29  *
30  *  This program is distributed in the hope that it will be useful,
31  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
32  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
33  */
34 #include "E.h"
35 #include "desktops.h"
36 #include "emodule.h"
37 #include "ewins.h"
38 #include "focus.h"
39 #include "grabs.h"
40 #include "iclass.h"
41 #include "icons.h"
42 #include "screen.h"
43 #include "tclass.h"
44 #include "tooltips.h"
45 #include "xwin.h"
46 #include <X11/keysym.h>
47
48 typedef struct {
49    EWin               *ewin;
50    Win                 win;
51    char               *txt;
52 } WarplistItem;
53
54 typedef struct {
55    EObj                o;
56    TextClass          *tc;
57    ImageClass         *ic;
58    int                 mw, mh, tw, th;
59 } WarpFocusWin;
60
61 static void         WarpFocusHandleEvent(Win win, XEvent * ev, void *prm);
62
63 static WarpFocusWin *warpFocusWindow = NULL;
64
65 static int          warpFocusIndex = 0;
66 static unsigned int warpFocusKey = 0;
67 static unsigned int warpFocusState = 0;
68 static int          warplist_num = 0;
69 static WarplistItem *warplist;
70
71 #define ICON_PAD 2
72
73 static WarpFocusWin *
74 WarpFocusWinCreate(void)
75 {
76    WarpFocusWin       *fw;
77
78    fw = ECALLOC(WarpFocusWin, 1);
79    if (!fw)
80       return fw;
81
82    EoInit(fw, EOBJ_TYPE_MISC, None, 0, 0, 1, 1, 1, "Warp");
83    EoSetFloating(fw, 1);
84    EoSetLayer(fw, 20);
85    EoSetFade(fw, 1);
86    EoSetShadow(fw, 1);
87
88    EventCallbackRegister(EoGetWin(fw), 0, WarpFocusHandleEvent, NULL);
89    ESelectInput(EoGetWin(fw), ButtonReleaseMask);
90
91    fw->tc = TextclassFind("WARPFOCUS", 0);
92    if (!fw->tc)
93       fw->tc = TextclassFind("COORDS", 1);
94
95    fw->ic = ImageclassFind("WARPFOCUS", 0);
96    if (!fw->ic)
97       fw->ic = ImageclassFind("COORDS", 1);
98
99    return fw;
100 }
101
102 #if 0
103 static void
104 WarpFocusWinDestroy(WarpFocusWin * fw)
105 {
106    EventCallbackUnregister(EoGetWin(fw), 0, WarpFocusHandleEvent, NULL);
107    EoFini(fw);
108    Efree(fw);
109 }
110 #endif
111
112 static void
113 WarpFocusWinShow(WarpFocusWin * fw)
114 {
115    WarplistItem       *wi;
116    EImageBorder       *pad;
117    EWin               *ewin;
118    int                 i, x, y, w, h, ww, hh;
119    char                s[1024], ss[32];
120    const char         *fmt;
121
122    w = 0;
123    h = 0;
124    pad = ImageclassGetPadding(fw->ic);
125
126    for (i = 0; i < warplist_num; i++)
127      {
128         wi = warplist + i;
129         wi->win = ECreateWindow(EoGetWin(fw), 0, 0, 1, 1, 0);
130         EMapWindow(wi->win);
131
132         ewin = wi->ewin;
133         if (ewin->state.iconified)
134            fmt = "%s[%s]";
135         else if (ewin->state.shaded)
136            fmt = "%s=%s=";
137         else
138            fmt = "%s%s";
139         ss[0] = '\0';
140         if (Conf.warplist.showalldesks)
141           {
142              if (EoIsSticky(ewin) || ewin->state.iconified)
143                 strcpy(ss, "[-] ");
144              else
145                 Esnprintf(ss, sizeof(ss), "[%d] ", EoGetDeskNum(ewin));
146           }
147         Esnprintf(s, sizeof(s), fmt, ss, EwinGetTitle(ewin));
148         wi->txt = strdup(s);
149         TextSize(fw->tc, 0, 0, 0, wi->txt, &ww, &hh, 17);
150         if (ww > w)
151            w = ww;
152         if (hh > h)
153            h = hh;
154      }
155
156    fw->tw = w;                  /* Text size */
157    fw->th = h;
158    w += pad->left + pad->right;
159    h += pad->top + pad->bottom;
160    if (Conf.warplist.icon_mode != 0)
161       w += h;
162    fw->mw = w;                  /* Focus list item size */
163    fw->mh = h;
164
165    /* Reset shape */
166    EShapeSetMask(EoGetWin(fw), 0, 0, None);
167
168    ScreenGetAvailableAreaByPointer(&x, &y, &ww, &hh);
169    x += (ww - w) / 2;
170    y += (hh - h * warplist_num) / 2;
171    EoMoveResize(fw, x, y, w, h * warplist_num);
172
173    for (i = 0; i < warplist_num; i++)
174       EMoveResizeWindow(warplist[i].win, 0, (h * i), fw->mw, fw->mh);
175
176    EoMap(fw, 0);
177
178    /*
179     * Grab the keyboard. The grab is automatically released when
180     * WarpFocusHide unmaps warpFocusWindow.
181     */
182    GrabKeyboardSet(EoGetWin(fw));
183    GrabPointerSet(EoGetWin(fw), None, 0);
184
185    TooltipsEnable(0);
186 }
187
188 static void
189 WarpFocusWinHide(WarpFocusWin * fw)
190 {
191    int                 i;
192
193    EoUnmap(fw);
194    for (i = 0; i < warplist_num; i++)
195      {
196         EDestroyWindow(warplist[i].win);
197         Efree(warplist[i].txt);
198      }
199 #if 0                           /* We might as well keep it around */
200    WarpFocusWinDestroy(fw);
201    warpFocusWindow = NULL;
202 #endif
203
204    TooltipsEnable(1);
205 }
206
207 static void
208 WarpFocusWinPaint(WarpFocusWin * fw)
209 {
210    int                 i, state, iw;
211    WarplistItem       *wi;
212    EImageBorder       *pad;
213
214    pad = ImageclassGetPadding(fw->ic);
215
216    for (i = 0; i < warplist_num; i++)
217      {
218         wi = warplist + i;
219
220         if (!EwinFindByPtr(wi->ewin))
221            wi->ewin = NULL;
222         if (!wi->ewin)
223            continue;
224
225         state = (i == warpFocusIndex) ? STATE_CLICKED : STATE_NORMAL;
226
227         ImageclassApply(fw->ic, wi->win, 0, 0, state, ST_WARPLIST);
228
229         iw = 0;
230         if (Conf.warplist.icon_mode != 0)
231           {
232              int                 icon_size = fw->mh - 2 * ICON_PAD;
233              EImage             *im;
234
235              im = EwinIconImageGet(wi->ewin, icon_size,
236                                    Conf.warplist.icon_mode);
237              if (im)
238                {
239                   EImageRenderOnDrawable(im, wi->win, None,
240                                          EIMAGE_BLEND | EIMAGE_ANTI_ALIAS,
241                                          pad->left + ICON_PAD, ICON_PAD,
242                                          icon_size, icon_size);
243                   EImageFree(im);
244                }
245              iw = fw->mh;
246           }
247
248         TextDraw(fw->tc, wi->win, None, 0, 0, state, wi->txt,
249                  pad->left + iw, pad->top, fw->tw, fw->th, 0, 0);
250      }
251
252    /* FIXME - Check shape */
253    EoShapeUpdate(fw, 1);
254 }
255
256 static void
257 WarpFocusShow(void)
258 {
259    WarpFocusWin       *fw = warpFocusWindow;
260
261    if (!warplist)
262       return;
263
264    if (!fw)
265      {
266         warpFocusWindow = fw = WarpFocusWinCreate();
267         if (!fw)
268            return;
269      }
270
271    if (!EoIsShown(fw))
272       WarpFocusWinShow(fw);
273
274    WarpFocusWinPaint(fw);
275 }
276
277 static void
278 WarpFocusHide(void)
279 {
280    WarpFocusWin       *fw = warpFocusWindow;
281
282    if (fw && EoIsShown(fw))
283       WarpFocusWinHide(fw);
284
285    Efree(warplist);
286    warplist = NULL;
287    warplist_num = 0;
288 }
289
290 void
291 WarpFocus(int delta)
292 {
293    WarpFocusWin       *fw = warpFocusWindow;
294    EWin               *const *lst;
295    EWin               *ewin;
296    int                 i, num;
297    WarplistItem       *wl;
298
299    /* Remember invoking keycode (ugly hack) */
300    if (!fw || !EoIsShown(fw))
301      {
302         warpFocusKey = Mode.events.last_keycode;
303         warpFocusState = Mode.events.last_keystate;
304      }
305
306    if (!warplist)
307      {
308         lst = EwinListFocusGet(&num);
309         for (i = 0; i < num; i++)
310           {
311              ewin = lst[i];
312              if (               /* Either visible or iconified */
313                    ((EwinIsOnScreen(ewin)) || (ewin->state.iconified) ||
314                     (Conf.warplist.showalldesks)) &&
315                    /* Exclude windows that explicitely say so */
316                    (!ewin->props.skip_focuslist) &&
317                    (!ewin->props.skip_ext_task) &&
318                    /* Keep shaded windows if conf say so */
319                    ((!ewin->state.shaded) || (Conf.warplist.showshaded)) &&
320                    /* Keep sticky windows if conf say so */
321                    ((!EoIsSticky(ewin)) || (Conf.warplist.showsticky)) &&
322                    /* Keep iconified windows if conf say so */
323                    ((!ewin->state.iconified) || (Conf.warplist.showiconified)))
324                {
325                   warplist_num++;
326                   warplist = EREALLOC(WarplistItem, warplist, warplist_num);
327                   wl = warplist + warplist_num - 1;
328                   wl->ewin = ewin;
329                }
330           }
331
332         /* Hmmm. Hack... */
333         if (warplist_num >= 2 && warplist[1].ewin == GetFocusEwin())
334           {
335              warplist[1].ewin = warplist[0].ewin;
336              warplist[0].ewin = GetFocusEwin();
337           }
338
339         warpFocusIndex = 0;
340      }
341
342    if (!warplist)
343       return;
344
345    warpFocusIndex = (warpFocusIndex + warplist_num + delta) % warplist_num;
346    ewin = warplist[warpFocusIndex].ewin;
347    if (!EwinFindByPtr(ewin))
348       ewin = NULL;
349    if (!ewin)
350       return;
351
352    WarpFocusShow();
353
354    if (!EwinIsOnScreen(ewin))
355       return;
356
357    if (Conf.focus.raise_on_next)
358       EwinRaise(ewin);
359    if (Conf.focus.warp_on_next)
360       EwinWarpTo(ewin);
361    if (Conf.warplist.warpfocused)
362       FocusToEWin(ewin, FOCUS_SET);
363 }
364
365 static void
366 WarpFocusClick(int ix)
367 {
368    EWin               *ewin;
369
370    if (!warplist)
371       return;
372    if (ix < 0 || ix >= warplist_num)
373       return;
374    if (ix == warpFocusIndex)
375       return;
376
377    warpFocusIndex = ix;
378    WarpFocusShow();
379
380    ewin = warplist[ix].ewin;
381    if (!EwinFindByPtr(ewin))
382       return;
383
384    if (Conf.focus.raise_on_next)
385       EwinRaise(ewin);
386
387    FocusToEWin(ewin, FOCUS_SET);
388 }
389
390 static void
391 WarpFocusFinish(void)
392 {
393    EWin               *ewin;
394
395    ewin = warplist[warpFocusIndex].ewin;
396
397    WarpFocusHide();
398
399    if (!EwinFindByPtr(ewin))
400       return;
401
402    EwinOpActivate(ewin, OPSRC_USER, Conf.warplist.raise_on_select);
403    if (Conf.warplist.warp_on_select)
404       EwinWarpTo(ewin);
405 }
406
407 static void
408 WarpFocusHandleEvent(Win win __UNUSED__, XEvent * ev, void *prm __UNUSED__)
409 {
410    WarpFocusWin       *fw = warpFocusWindow;
411    KeySym              key;
412    unsigned int        mask;
413
414    if (!EoIsShown(fw))
415       return;
416
417    switch (ev->type)
418      {
419      case KeyPress:
420         if (ev->xkey.keycode == warpFocusKey)
421           {
422              if (((ev->xkey.state ^ warpFocusState) &
423                   Mode.masks.mod_key_mask) == 0)
424                 key = 0x80000000;
425              else
426                 key = 0x80000001;
427           }
428         else
429            key = XLookupKeysym(&ev->xkey, 0);
430         switch (key)
431           {
432           default:
433              break;
434           case 0x80000000:
435           case XK_Down:
436              WarpFocus(1);
437              break;
438           case XK_Up:
439           case 0x80000001:
440              WarpFocus(-1);
441              break;
442           }
443         break;
444
445      case KeyRelease:
446         mask = 0;
447         EQueryPointer(NULL, NULL, NULL, NULL, &mask);
448         if ((mask & Mode.masks.mod_key_mask) == 0)
449           {
450              WarpFocusFinish();
451              break;
452           }
453         if (ev->xkey.keycode == warpFocusKey)
454            key = 0x80000000;
455         else
456            key = XLookupKeysym(&ev->xkey, 0);
457         switch (key)
458           {
459           case XK_Escape:
460              WarpFocusHide();
461              break;
462           default:
463           case 0x80000000:
464           case XK_Down:
465           case XK_Up:
466              break;
467           }
468         break;
469
470      case ButtonRelease:
471         WarpFocusClick((ev->xbutton.y * warplist_num) / EoGetH(fw));
472         break;
473      }
474 }
475
476 /*
477  * Warplist module
478  */
479
480 static void
481 WarplistCfgValidate(void)
482 {
483    if (Conf.warplist.icon_mode < 0 || Conf.warplist.icon_mode > 3)
484       Conf.warplist.icon_mode = 3;
485 }
486
487 static void
488 WarplistSighan(int sig, void *prm __UNUSED__)
489 {
490    switch (sig)
491      {
492      case ESIGNAL_INIT:
493         WarplistCfgValidate();
494         break;
495      }
496 }
497
498 static const CfgItem WarplistCfgItems[] = {
499    CFG_ITEM_BOOL(Conf.warplist, enable, 1),
500    CFG_ITEM_BOOL(Conf.warplist, showsticky, 1),
501    CFG_ITEM_BOOL(Conf.warplist, showshaded, 1),
502    CFG_ITEM_BOOL(Conf.warplist, showiconified, 1),
503    CFG_ITEM_BOOL(Conf.warplist, showalldesks, 0),
504    CFG_ITEM_BOOL(Conf.warplist, warpfocused, 1),
505    CFG_ITEM_BOOL(Conf.warplist, raise_on_select, 1),
506    CFG_ITEM_BOOL(Conf.warplist, warp_on_select, 0),
507    CFG_ITEM_INT(Conf.warplist, icon_mode, 3),
508 };
509 #define N_CFG_ITEMS (sizeof(WarplistCfgItems)/sizeof(CfgItem))
510
511 extern const EModule ModWarplist;
512 const EModule       ModWarplist = {
513    "warplist", "warp",
514    WarplistSighan,
515    {0, NULL},
516    {N_CFG_ITEMS, WarplistCfgItems}
517 };