chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / draw.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2007-2008 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 "desktops.h"
26 #include "eobj.h"
27 #include "ewins.h"
28 #include "piximg.h"
29 #include "xwin.h"
30
31 #if 0
32 #include <X11/bitmaps/gray>
33 #include <X11/bitmaps/gray3>
34 #else
35 /* Include contents of X11/bitmaps/gray+gray3.
36  * (avoid build failure if x11 bitmap package isn't installed) */
37
38 #define gray_width 2
39 #define gray_height 2
40 static const char   gray_bits[] = { 0x01, 0x02 };
41
42 #define gray3_width 4
43 #define gray3_height 4
44 static const char   gray3_bits[] = { 0x01, 0x00, 0x04, 0x00 };
45 #endif
46
47 typedef struct {
48    EObj                o;
49    Pixmap              mask;
50    GC                  gc;
51 } ShapeWin;
52
53 static Font         font = None;        /* Used in mode 1 (technical) */
54
55 #define DRAW_H_ARROW(_dr, _gc, x1, x2, y1) \
56     if (((x2) - (x1)) >= 12) \
57       { \
58         XDrawLine(disp, _dr, _gc, (x1), (y1), (x1) + 6, (y1) - 3); \
59         XDrawLine(disp, _dr, _gc, (x1), (y1), (x1) + 6, (y1) + 3); \
60         XDrawLine(disp, _dr, _gc, (x2), (y1), (x2) - 6, (y1) - 3); \
61         XDrawLine(disp, _dr, _gc, (x2), (y1), (x2) - 6, (y1) + 3); \
62       } \
63     if ((x2) >= (x1)) \
64       { \
65         XDrawLine(disp, _dr, _gc, (x1), (y1), (x2), (y1)); \
66         Esnprintf(str, sizeof(str), "%i", (x2) - (x1) + 1); \
67         XDrawString(disp, _dr, _gc, ((x1) + (x2)) / 2, (y1) - 10, str, strlen(str)); \
68       }
69 #define DRAW_V_ARROW(_dr, _gc, y1, y2, x1) \
70     if (((y2) - (y1)) >= 12) \
71       { \
72         XDrawLine(disp, _dr, _gc, (x1), (y1), (x1) + 3, (y1) + 6); \
73         XDrawLine(disp, _dr, _gc, (x1), (y1), (x1) - 3, (y1) + 6); \
74         XDrawLine(disp, _dr, _gc, (x1), (y2), (x1) + 3, (y2) - 6); \
75         XDrawLine(disp, _dr, _gc, (x1), (y2), (x1) - 3, (y2) - 6); \
76       } \
77     if ((y2) >= (y1)) \
78       { \
79         XDrawLine(disp, _dr, _gc, (x1), (y1), (x1), (y2)); \
80         Esnprintf(str, sizeof(str), "%i", (y2) - (y1) + 1); \
81         XDrawString(disp, _dr, _gc, (x1) + 10, ((y1) + (y2)) / 2, str, strlen(str)); \
82       }
83
84 #define DO_DRAW_MODE_1(_dr, _gc, _a, _b, _c, _d) \
85   do { \
86     if (!font) \
87       font = XLoadFont(disp, "-*-helvetica-medium-r-*-*-10-*-*-*-*-*-*-*"); \
88     XSetFont(disp, _gc, font); \
89     if (_c < 3) _c = 3; \
90     if (_d < 3) _d = 3; \
91     DRAW_H_ARROW(_dr, _gc, _a + bl, _a + bl + _c - 1, _b + bt + _d - 16); \
92     DRAW_H_ARROW(_dr, _gc, 0, _a - 1, _b + bt + (_d / 2)); \
93     DRAW_H_ARROW(_dr, _gc, _a + _c + bl + br, WinGetW(VROOT) - 1, _b + bt + (_d / 2)); \
94     DRAW_V_ARROW(_dr, _gc, _b + bt, _b + bt + _d - 1, _a + bl + 16); \
95     DRAW_V_ARROW(_dr, _gc, 0, _b - 1, _a + bl + (_c / 2)); \
96     DRAW_V_ARROW(_dr, _gc, _b + _d + bt + bb, WinGetH(VROOT) - 1, _a + bl + (_c / 2)); \
97     XDrawLine(disp, _dr, _gc, _a, 0, _a, WinGetH(VROOT)); \
98     XDrawLine(disp, _dr, _gc, _a + _c + bl + br - 1, 0, _a + _c + bl + br - 1, WinGetH(VROOT)); \
99     XDrawLine(disp, _dr, _gc, 0, _b, WinGetW(VROOT), _b); \
100     XDrawLine(disp, _dr, _gc, 0, _b + _d + bt + bb - 1, WinGetW(VROOT), _b + _d + bt + bb - 1); \
101     XDrawRectangle(disp, _dr, _gc, _a + bl + 1, _b + bt + 1, _c - 3, _d - 3); \
102   } while(0)
103
104 #define DO_DRAW_MODE_2(_dr, _gc, _a, _b, _c, _d) \
105   do { \
106     if (_c < 3) _c = 3; \
107     if (_d < 3) _d = 3; \
108     XDrawRectangle(disp, _dr, _gc, _a, _b, _c + bl + br - 1, _d + bt + bb - 1); \
109     XDrawRectangle(disp, _dr, _gc, _a + bl + 1, _b + bt + 1, _c - 3, _d - 3); \
110   } while(0)
111
112 #define DO_DRAW_MODE_3(_dr, _gc, _a, _b, _c, _d) \
113   do { \
114     XSetFillStyle(disp, _gc, FillStippled); \
115     XSetStipple(disp, _gc, b2); \
116     if ((_c + bl + br > 0) && (bt > 0)) \
117       XFillRectangle(disp, _dr, _gc, _a, _b, _c + bl + br, bt); \
118     if ((_c + bl + br > 0) && (bb > 0)) \
119       XFillRectangle(disp, _dr, _gc, _a, _b + _d + bt, _c + bl + br, bb); \
120     if ((_d > 0) && (bl > 0)) \
121       XFillRectangle(disp, _dr, _gc, _a, _b + bt, bl, _d); \
122     if ((_d > 0) && (br > 0)) \
123       XFillRectangle(disp, _dr, _gc, _a + _c + bl, _b + bt, br, _d); \
124     XSetStipple(disp, _gc, b3); \
125     if ((_c > 0) && (_d > 0)) \
126       XFillRectangle(disp, _dr, _gc, _a + bl + 1, _b + bt + 1, _c - 3, _d - 3); \
127   } while(0)
128
129 #define DO_DRAW_MODE_4(_dr, _gc, _a, _b, _c, _d) \
130   do { \
131     XSetFillStyle(disp, _gc, FillStippled); \
132     XSetStipple(disp, _gc, b2); \
133     XFillRectangle(disp, _dr, _gc, _a, _b, _c + bl + br, _d + bt + bb); \
134   } while(0)
135
136 #define _SHAPE_SET_RECT(rl, _x, _y, _w, _h) \
137   do { \
138     rl[0].x = (_x);        rl[0].y = (_y);        rl[0].width = (_w); rl[0].height = 1; \
139     rl[1].x = (_x);        rl[1].y = (_y)+(_h)-1; rl[1].width = (_w); rl[1].height = 1; \
140     rl[2].x = (_x);        rl[2].y = (_y)+1;      rl[2].width = 1;    rl[2].height = (_h)-2; \
141     rl[3].x = (_x)+(_w)-1; rl[3].y = (_y)+1;      rl[3].width = 1;    rl[3].height = (_h)-2; \
142   } while(0)
143
144 #define _R(x) (((x) >> 16) & 0xff)
145 #define _G(x) (((x) >>  8) & 0xff)
146 #define _B(x) (((x)      ) & 0xff)
147
148 static unsigned int
149 _ShapeGetColor(void)
150 {
151    static char         color_valid = 0;
152    static unsigned int color_value = 0;
153    static unsigned int color_pixel;
154    EColor              color;
155
156    if (color_valid && color_value == Conf.movres.color)
157       goto done;
158
159    color_value = Conf.movres.color;
160    SET_COLOR(&color, _R(color_value), _G(color_value), _B(color_value));
161    EAllocColor(WinGetCmap(VROOT), &color);
162    color_pixel = color.pixel;
163    color_valid = 1;
164
165  done:
166    return color_pixel;
167 }
168
169 static void
170 _ShapeWinDestroy(ShapeWin * sw)
171 {
172    EoUnmap(sw);
173    EoFini(sw);
174    if (sw->gc)
175       EXFreeGC(sw->gc);
176    if (sw->mask != None)
177       EFreePixmap(sw->mask);
178    Efree(sw);
179 }
180
181 static ShapeWin    *
182 _ShapeWinCreate(int md)
183 {
184    ShapeWin           *sw;
185
186    sw = ECALLOC(ShapeWin, 1);
187    if (!sw)
188       return NULL;
189
190    EoInit(sw, EOBJ_TYPE_MISC, None,
191           0, 0, WinGetW(VROOT), WinGetH(VROOT), 2, "Wires");
192    if (!EoGetWin(sw))
193       goto bail_out;
194
195    EoSetFloating(sw, 1);
196    EoSetLayer(sw, 18);
197    ESetWindowBackground(EoGetWin(sw), _ShapeGetColor());
198 #ifdef ShapeInput               /* Should really check server too */
199    XShapeCombineRectangles(disp, EoGetXwin(sw),
200                            ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted);
201 #endif
202
203    if (md == 1)
204      {
205         sw->mask =
206            ECreatePixmap(EoGetWin(sw), WinGetW(VROOT), WinGetH(VROOT), 1);
207         sw->gc = EXCreateGC(sw->mask, 0, NULL);
208         if (sw->mask == None || !sw->gc)
209            goto bail_out;
210      }
211
212    return sw;
213
214  bail_out:
215    _ShapeWinDestroy(sw);
216    return NULL;
217 }
218
219 static void
220 _ShapeSet(ShapeWin * sw, int md, int x, int y, int w, int h,
221           int bl, int br, int bt, int bb, int seqno)
222 {
223    int                 w2, h2;
224
225    w2 = w + bl + br;
226    h2 = h + bt + bb;
227
228    if (md == 1)
229      {
230         char                str[32];
231
232         XSetForeground(disp, sw->gc, 0);
233         XFillRectangle(disp, sw->mask, sw->gc,
234                        0, 0, WinGetW(VROOT), WinGetH(VROOT));
235         XSetForeground(disp, sw->gc, 1);
236         DO_DRAW_MODE_1(sw->mask, sw->gc, x, y, w, h);
237         if (seqno == 0)
238            EShapeSetMask(EoGetWin(sw), 0, 0, sw->mask);
239         else
240            EShapeUnionMask(EoGetWin(sw), 0, 0, sw->mask);
241      }
242    else
243      {
244         XRectangle          rl[8];
245
246         _SHAPE_SET_RECT((&rl[0]), x, y, w2, h2);
247         w = (w > 5) ? w - 2 : 3;
248         h = (h > 5) ? h - 2 : 3;
249         _SHAPE_SET_RECT((&rl[4]), x + bl + 1, y + bt + 1, w, h);
250
251         if (seqno == 0)
252            EShapeSetRects(EoGetWin(sw), 0, 0, rl, 8);
253         else
254            EShapeUnionRects(EoGetWin(sw), 0, 0, rl, 8);
255      }
256    EoShapeUpdate(sw, 0);
257 }
258
259 static PixImg      *root_pi = NULL;
260 static PixImg      *ewin_pi = NULL;
261 static PixImg      *draw_pi = NULL;
262
263 static void
264 _PixImgsCreate(Window root, const EWin * ewin)
265 {
266    root_pi = ECreatePixImg(root, WinGetW(VROOT), WinGetH(VROOT));
267    ewin_pi = ECreatePixImg(root, EoGetW(ewin), EoGetH(ewin));
268    draw_pi = ECreatePixImg(root, EoGetW(ewin), EoGetH(ewin));
269 }
270
271 static void
272 _PixImgsDestroy(void)
273 {
274    EDestroyPixImg(root_pi);
275    EDestroyPixImg(ewin_pi);
276    EDestroyPixImg(draw_pi);
277    EBlendRemoveShape(NULL, 0, 0, 0);
278    EBlendPixImg(NULL, NULL, NULL, NULL, 0, 0, 0, 0);
279    root_pi = NULL;
280    ewin_pi = NULL;
281    draw_pi = NULL;
282 }
283
284 void
285 DrawEwinShape(EWin * ewin, int md, int x, int y, int w, int h,
286               int firstlast, int seqno)
287 {
288    static GC           gc = 0;
289    static Pixmap       b2 = 0, b3 = 0;
290    Window              root = WinGetXwin(VROOT);
291    int                 x1, y1, w1, h1, dx, dy;
292    int                 bl, br, bt, bb;
293    char                str[32];
294
295    /* Quit if no change */
296    if (firstlast == 1 &&
297        (x == ewin->shape_x && y == ewin->shape_y &&
298         (ewin->state.shaded || (w == ewin->shape_w && h == ewin->shape_h))))
299       return;
300
301    switch (md)
302      {
303      case 0:
304         EwinOpMoveResize(ewin, OPSRC_USER, x, y, w, h);
305         EwinShapeSet(ewin);
306         CoordsShow(ewin);
307         goto done;
308      case 1:
309      case 2:
310         break;
311      case 3:
312      case 4:
313         if (!b2)
314            b2 = XCreateBitmapFromData(disp, root, gray_bits, gray_width,
315                                       gray_height);
316         if (!b3)
317            b3 = XCreateBitmapFromData(disp, root, gray3_bits, gray3_width,
318                                       gray3_height);
319         break;
320      case 5:
321         break;
322      }
323
324    if (firstlast == 0)
325       EwinShapeSet(ewin);
326
327    dx = EoGetX(EoGetDesk(ewin));
328    dy = EoGetY(EoGetDesk(ewin));
329    x1 = ewin->shape_x + dx;
330    y1 = ewin->shape_y + dy;
331
332    w1 = ewin->shape_w;
333    h1 = ewin->shape_h;
334
335    ewin->shape_x = x;
336    ewin->shape_y = y;
337    x += dx;
338    y += dy;
339
340    if (!ewin->state.shaded)
341      {
342         ewin->shape_w = w;
343         ewin->shape_h = h;
344      }
345    else
346      {
347         w = ewin->shape_w;
348         h = ewin->shape_h;
349      }
350
351    EwinBorderGetSize(ewin, &bl, &br, &bt, &bb);
352
353    if (md <= 2 && Conf.movres.avoid_server_grab)
354      {
355         static ShapeWin    *shape_win = NULL;
356
357         if (firstlast == 0 && !shape_win)
358            shape_win = _ShapeWinCreate(md);
359         if (!shape_win)
360            return;
361
362         _ShapeSet(shape_win, md, x, y, w, h, bl, br, bt, bb, seqno);
363         EoMap(shape_win, 0);
364
365         CoordsShow(ewin);
366
367         if (firstlast == 2)
368           {
369              _ShapeWinDestroy(shape_win);
370              shape_win = NULL;
371           }
372         goto done;
373      }
374
375    if (!gc)
376      {
377         XGCValues           gcv;
378
379         gcv.function = GXxor;
380         gcv.foreground = Dpy.pixel_white;
381         if (gcv.foreground == 0)
382            gcv.foreground = Dpy.pixel_black;
383         gcv.subwindow_mode = IncludeInferiors;
384         gc = EXCreateGC(root,
385                         GCFunction | GCForeground | GCSubwindowMode, &gcv);
386      }
387
388    switch (md)
389      {
390      case 1:
391         if (firstlast > 0)
392            DO_DRAW_MODE_1(root, gc, x1, y1, w1, h1);
393         CoordsShow(ewin);
394         if (firstlast < 2)
395            DO_DRAW_MODE_1(root, gc, x, y, w, h);
396         break;
397      case 2:
398         if (firstlast > 0)
399            DO_DRAW_MODE_2(root, gc, x1, y1, w1, h1);
400         CoordsShow(ewin);
401         if (firstlast < 2)
402            DO_DRAW_MODE_2(root, gc, x, y, w, h);
403         break;
404      case 3:
405         if (firstlast > 0)
406            DO_DRAW_MODE_3(root, gc, x1, y1, w1, h1);
407         CoordsShow(ewin);
408         if (firstlast < 2)
409            DO_DRAW_MODE_3(root, gc, x, y, w, h);
410         break;
411      case 4:
412         if (firstlast > 0)
413            DO_DRAW_MODE_4(root, gc, x1, y1, w1, h1);
414         CoordsShow(ewin);
415         if (firstlast < 2)
416            DO_DRAW_MODE_4(root, gc, x, y, w, h);
417         break;
418      case 5:
419         {
420            if (firstlast == 0)
421              {
422                 GC                  gc2;
423
424                 _PixImgsDestroy();
425                 _PixImgsCreate(root, ewin);
426                 if ((!root_pi) || (!ewin_pi) || (!draw_pi))
427                   {
428                      _PixImgsDestroy();
429                      Conf.movres.mode_move = 0;
430                      EUngrabServer();
431                      DrawEwinShape(ewin, Conf.movres.mode_move, x, y, w, h,
432                                    firstlast, seqno);
433                      return;
434                   }
435                 EFillPixmap(root, root_pi->pmap, x1, y1, EoGetW(ewin),
436                             EoGetH(ewin));
437                 gc2 = EXCreateGC(root_pi->pmap, 0, NULL);
438                 XCopyArea(disp, root_pi->pmap, ewin_pi->pmap, gc2, x1, y1,
439                           EoGetW(ewin), EoGetH(ewin), 0, 0);
440                 EXFreeGC(gc2);
441                 EBlendPixImg(EoGetWin(ewin), root_pi, ewin_pi, draw_pi, x, y,
442                              EoGetW(ewin), EoGetH(ewin));
443              }
444            else if (firstlast == 1)
445              {
446                 int                 wt, ht;
447                 int                 adx, ady;
448
449                 dx = x - x1;
450                 dy = y - y1;
451                 if (dx < 0)
452                    adx = -dx;
453                 else
454                    adx = dx;
455                 if (dy < 0)
456                    ady = -dy;
457                 else
458                    ady = dy;
459                 wt = EoGetW(ewin);
460                 ht = EoGetH(ewin);
461                 if ((adx <= wt) && (ady <= ht))
462                   {
463                      if (dx < 0)
464                         EFillPixmap(root, root_pi->pmap, x, y, -dx, ht);
465                      else if (dx > 0)
466                         EFillPixmap(root, root_pi->pmap, x + wt - dx, y,
467                                     dx, ht);
468                      if (dy < 0)
469                         EFillPixmap(root, root_pi->pmap, x, y, wt, -dy);
470                      else if (dy > 0)
471                         EFillPixmap(root, root_pi->pmap, x, y + ht - dy,
472                                     wt, dy);
473                   }
474                 else
475                    EFillPixmap(root, root_pi->pmap, x, y, wt, ht);
476                 if ((adx <= wt) && (ady <= ht))
477                   {
478                      EBlendPixImg(EoGetWin(ewin), root_pi, ewin_pi, draw_pi,
479                                   x, y, EoGetW(ewin), EoGetH(ewin));
480                      if (dx > 0)
481                         EPastePixmap(root, root_pi->pmap, x1, y1, dx, ht);
482                      else if (dx < 0)
483                         EPastePixmap(root, root_pi->pmap, x1 + wt + dx,
484                                      y1, -dx, ht);
485                      if (dy > 0)
486                         EPastePixmap(root, root_pi->pmap, x1, y1, wt, dy);
487                      else if (dy < 0)
488                         EPastePixmap(root, root_pi->pmap, x1,
489                                      y1 + ht + dy, wt, -dy);
490                   }
491                 else
492                   {
493                      EPastePixmap(root, root_pi->pmap, x1, y1, wt, ht);
494                      EBlendPixImg(EoGetWin(ewin), root_pi, ewin_pi, draw_pi,
495                                   x, y, EoGetW(ewin), EoGetH(ewin));
496                   }
497                 EBlendRemoveShape(EoGetWin(ewin), root_pi->pmap, x, y);
498              }
499            else if (firstlast == 2)
500              {
501                 EPastePixmap(root, root_pi->pmap, x1, y1, EoGetW(ewin),
502                              EoGetH(ewin));
503                 _PixImgsDestroy();
504              }
505            else if (firstlast == 3)
506              {
507                 EPastePixmap(root, root_pi->pmap, x, y, EoGetW(ewin),
508                              EoGetH(ewin));
509                 if (root_pi)
510                    EDestroyPixImg(root_pi);
511                 root_pi = NULL;
512              }
513            else if (firstlast == 4)
514              {
515                 int                 wt, ht;
516
517                 wt = EoGetW(ewin);
518                 ht = EoGetH(ewin);
519                 root_pi = ECreatePixImg(root, WinGetW(VROOT), WinGetH(VROOT));
520                 EFillPixmap(root, root_pi->pmap, x, y, wt, ht);
521                 EBlendPixImg(EoGetWin(ewin), root_pi, ewin_pi, draw_pi, x, y,
522                              EoGetW(ewin), EoGetH(ewin));
523              }
524            CoordsShow(ewin);
525         }
526         break;
527      }
528
529    if (firstlast == 2)
530      {
531         EXFreeGC(gc);
532         gc = 0;
533      }
534
535  done:
536    if (firstlast == 0 || firstlast == 2 || firstlast == 4)
537      {
538         ewin->req_x = ewin->shape_x;
539         ewin->req_y = ewin->shape_y;
540         if (firstlast == 2)
541            CoordsHide();
542      }
543 }