1 #include "XlibWindow.h"
2 #include "classicui.h"
3 #include <cairo-xlib.h>
4 #include <X11/extensions/shape.h>
5 
FcitxXlibWindowCreate(FcitxClassicUI * classicui,size_t size)6 void* FcitxXlibWindowCreate(FcitxClassicUI* classicui, size_t size)
7 {
8     FcitxXlibWindow* window = fcitx_utils_malloc0(size);
9     window->owner = classicui;
10     return window;
11 }
12 
FcitxXlibWindowInit(FcitxXlibWindow * window,uint width,uint height,int x,int y,char * name,FcitxXWindowType windowType,FcitxWindowBackground * background,long int inputMask,FcitxMoveWindowFunc moveWindow,FcitxCalculateContentSizeFunc calculateContentSize,FcitxPaintContentFunc paintContent)13 void FcitxXlibWindowInit(FcitxXlibWindow* window,
14                          uint width, uint height,
15                          int x, int y,
16                          char* name,
17                          FcitxXWindowType windowType,
18                          FcitxWindowBackground* background,
19                          long int inputMask,
20                          FcitxMoveWindowFunc moveWindow,
21                          FcitxCalculateContentSizeFunc calculateContentSize,
22                          FcitxPaintContentFunc paintContent
23                         )
24 {
25     FcitxClassicUI* classicui = window->owner;
26     int depth;
27     Colormap cmap;
28     int iScreen = classicui->iScreen;
29     Display* dpy = classicui->dpy;
30     FcitxSkin *sc = &classicui->skin;
31     window->wId = None;
32     window->height = height;
33     window->width = width;
34     window->background = background;
35     window->MoveWindow = moveWindow;
36     window->CalculateContentSize = calculateContentSize;
37     window->paintContent = paintContent;
38     window->oldContentHeight = 0;
39     window->oldContentWidth = 0;
40 
41     SkinImage *back = NULL;
42 
43     if (background) {
44         back = LoadImage(sc, background->background, false);
45         /* for cache reason */
46         LoadImage(sc, background->overlay, false);
47     }
48 
49     if (back) {
50         window->width = cairo_image_surface_get_width(back->image);
51         window->height = cairo_image_surface_get_height(back->image);
52     }
53 
54     if (window->width == 0) {
55         window->width = 1;
56     }
57 
58     if (window->height == 0) {
59         window->height = 1;
60     }
61 
62     Visual* vs = ClassicUIFindARGBVisual(classicui);
63 
64     XSetWindowAttributes attrib;
65     unsigned long        attribmask;
66     FcitxX11InitWindowAttribute(classicui->owner, &vs, &cmap, &attrib, &attribmask, &depth);
67 
68     window->wId = XCreateWindow(dpy,
69                                 RootWindow(dpy, iScreen),
70                                 x, y,
71                                 window->width,
72                                 window->height,
73                                 0,
74                                 depth, InputOutput,
75                                 vs, attribmask,
76                                 &attrib);
77 
78     window->xlibSurface = cairo_xlib_surface_create(dpy, window->wId, vs, window->width, window->height);
79     window->contentSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, window->width, window->height);
80     window->backgroundSurface = cairo_surface_create_similar(window->contentSurface, CAIRO_CONTENT_COLOR_ALPHA,
81                                                                     window->width, window->height);
82 
83     XSelectInput(dpy, window->wId, inputMask);
84 
85     ClassicUISetWindowProperty(classicui, window->wId, windowType, name);
86 }
87 
88 
89 static inline
RectUnion(FcitxRect rt1,FcitxRect rt2)90 FcitxRect RectUnion(FcitxRect rt1, FcitxRect rt2)
91 {
92     FcitxRect rt = {
93         FCITX_MIN(rt1.x1,rt2.x1),
94         FCITX_MIN(rt1.y1,rt2.y1),
95         FCITX_MAX(rt1.x2,rt2.x2),
96         FCITX_MAX(rt1.y2,rt2.y2)
97     };
98 
99     return rt;
100 }
101 
FcitxXlibWindowPaintBackground(FcitxXlibWindow * window,cairo_t * c,unsigned int offX,unsigned int offY,unsigned int contentWidth,unsigned int contentHeight,unsigned int overlayX,unsigned int overlayY)102 void FcitxXlibWindowPaintBackground(FcitxXlibWindow* window,
103                                     cairo_t* c,
104                                     unsigned int offX, unsigned int offY,
105                                     unsigned int contentWidth, unsigned int contentHeight,
106                                     unsigned int overlayX, unsigned int overlayY
107                                    )
108 {
109     FcitxClassicUI* classicui = window->owner;
110     /* just clean the background with nothing */
111     cairo_save(c);
112     cairo_set_source_rgba(c, 0, 0, 0, 0);
113     cairo_set_operator(c, CAIRO_OPERATOR_SOURCE);
114     cairo_paint(c);
115     cairo_restore(c);
116     int backgrondWidth = contentWidth;
117     int backgrondHeight = contentHeight;
118     do {
119         SkinImage* back = NULL;
120         if (!window->background || (back = LoadImage(&window->owner->skin, window->background->background, false)) == NULL) {
121             break;
122         }
123 
124         backgrondWidth += window->background->marginLeft + window->background->marginRight;
125         backgrondHeight += window->background->marginTop + window->background->marginBottom;
126 
127         if (window->epoch != classicui->epoch || window->oldContentWidth != contentWidth || window->oldContentHeight != contentHeight) {
128             window->epoch = classicui->epoch;
129             window->oldContentHeight = contentHeight;
130             window->oldContentWidth = contentWidth;
131 
132             EnlargeCairoSurface(&window->backgroundSurface, backgrondWidth, backgrondHeight);
133 
134             cairo_t* backgroundcr = cairo_create(window->backgroundSurface);
135             DrawResizableBackground(backgroundcr,
136                                     back->image,
137                                     backgrondWidth, backgrondHeight,
138                                     window->background->marginLeft,
139                                     window->background->marginTop,
140                                     window->background->marginRight,
141                                     window->background->marginBottom,
142                                     window->background->fillV,
143                                     window->background->fillH
144                                 );
145             cairo_destroy(backgroundcr);
146             cairo_surface_flush(window->backgroundSurface);
147         }
148 
149         cairo_save(c);
150         cairo_translate(c, offX, offY);
151         cairo_set_operator(c, CAIRO_OPERATOR_SOURCE);
152         cairo_set_source_surface(c, window->backgroundSurface, 0, 0);
153         cairo_rectangle(c, 0, 0, backgrondWidth, backgrondHeight);
154         cairo_clip(c);
155         cairo_paint(c);
156         cairo_restore(c);
157 
158     } while (0);
159 
160     SkinImage* overlay = NULL;
161     do {
162         if (!window->background || (overlay = LoadImage(&window->owner->skin, window->background->overlay, false)) == NULL) {
163             break;
164         }
165 
166         cairo_save(c);
167         cairo_translate(c, overlayX, overlayY);
168         cairo_set_operator(c, CAIRO_OPERATOR_OVER);
169         cairo_set_source_surface(c, overlay->image, 0, 0);
170         cairo_rectangle(c, 0, 0, cairo_image_surface_get_width(overlay->image), cairo_image_surface_get_height(overlay->image));
171         cairo_clip(c);
172         cairo_paint(c);
173         cairo_restore(c);
174     } while (0);
175 
176     if (classicui->hasXShape) {
177         if (overlay
178             || window->background->clickMarginLeft != 0
179             || window->background->clickMarginRight != 0
180             || window->background->clickMarginTop != 0
181             || window->background->clickMarginBottom != 0) {
182             XRectangle r[1];
183             r[0].x = 0;
184             r[0].y = 0;
185             r[0].width = backgrondWidth - window->background->clickMarginLeft - window->background->clickMarginRight;
186             r[0].height = backgrondHeight - window->background->clickMarginTop - window->background->clickMarginBottom;
187             XShapeCombineRectangles(classicui->dpy, window->wId, ShapeInput,
188                                     offX + window->background->clickMarginLeft,
189                                     offY + window->background->clickMarginTop,
190                                     r, 1, ShapeSet, Unsorted
191                                    );
192         } else {
193             XShapeCombineMask(classicui->dpy, window->wId, ShapeInput, 0, 0,
194                               None, ShapeSet);
195         }
196     }
197 }
198 
FcitxXlibWindowPaint(FcitxXlibWindow * window)199 void FcitxXlibWindowPaint(FcitxXlibWindow* window)
200 {
201     FcitxClassicUI* classicui = window->owner;
202     FcitxSkin *sc = &classicui->skin;
203     int oldWidth = window->width, oldHeight = window->height;
204     unsigned int contentHeight = 0, contentWidth = 0;
205     window->CalculateContentSize(window, &contentWidth, &contentHeight);
206     FcitxRect rect, mergedRect;
207     rect.x1 = 0;
208     rect.y1 = 0;
209     rect.x2 = contentWidth + (window->background ? (window->background->marginLeft + window->background->marginRight) : 0);
210     rect.y2 = contentHeight + (window->background ? (window->background->marginTop + window->background->marginBottom) : 0);
211     mergedRect = rect;
212 
213     unsigned int overlayX = 0, overlayY = 0;
214     unsigned int offX = 0, offY = 0;
215     SkinImage* overlayImage = NULL;
216     if (window->background) {
217         if (window->background->overlay[0]) {
218             overlayImage =  LoadImage(sc, window->background->overlay, false);
219         }
220 
221         unsigned int overlayW = 0, overlayH = 0;
222     #define _POS(P, X, Y) case P: overlayX = X; overlayY = Y; break;
223         switch (window->background->dock) {
224             _POS(OD_TopLeft, 0, 0)
225             _POS(OD_TopCenter, rect.x2 / 2, 0)
226             _POS(OD_TopRight, rect.x2, 0)
227             _POS(OD_CenterLeft, 0, rect.y2 / 2)
228             _POS(OD_Center, rect.x2 / 2, rect.y2 / 2)
229             _POS(OD_CenterRight, rect.x2, rect.y2 / 2)
230             _POS(OD_BottomLeft, 0, rect.y2)
231             _POS(OD_BottomCenter, rect.x2 / 2, rect.y2)
232             _POS(OD_BottomRight, rect.x2, rect. y2)
233         }
234 
235         overlayX += window->background->overlayOffsetX;
236         overlayY += window->background->overlayOffsetY;
237 
238         if (overlayImage) {
239             overlayW = cairo_image_surface_get_width(overlayImage->image);
240             overlayH = cairo_image_surface_get_height(overlayImage->image);
241         }
242 
243         FcitxRect overlayRect = {overlayX, overlayY, overlayX + overlayW, overlayY + overlayH };
244         mergedRect = RectUnion(rect, overlayRect);
245         offX = 0 - mergedRect.x1;
246         offY = 0 - mergedRect.y1;
247         overlayX = overlayX - mergedRect.x1;
248         overlayY = overlayY - mergedRect.y1;
249     }
250 
251     int width = mergedRect.x2 - mergedRect.x1;
252     int height = mergedRect.y2 - mergedRect.y1;
253 
254     if (width <= 0) {
255         width = 1;
256     }
257     if (height <= 0) {
258         height = 1;
259     }
260 
261     EnlargeCairoSurface(&window->contentSurface, width, height);
262     cairo_t* c = cairo_create(window->contentSurface);
263     FcitxXlibWindowPaintBackground(window, c, offX, offY, contentWidth, contentHeight, overlayX, overlayY);
264 
265     if (overlayImage) {
266         cairo_save(c);
267         cairo_set_operator(c, CAIRO_OPERATOR_OVER);
268         cairo_set_source_surface(c, overlayImage->image, overlayX, overlayY);
269         cairo_paint(c);
270         cairo_restore(c);
271     }
272 
273     window->contentX = offX + (window->background ? window->background->marginLeft : 0);
274     window->contentY = offY + (window->background ? window->background->marginTop : 0);
275     window->contentWidth = contentWidth;
276     window->contentHeight = contentHeight;
277     cairo_save(c);
278     cairo_translate(c, window->contentX, window->contentY);
279     window->paintContent(window, c);
280     cairo_restore(c);
281     cairo_destroy(c);
282     cairo_surface_flush(window->contentSurface);
283 
284     boolean resizeFlag = false;
285     if (width != oldWidth || height != oldHeight) {
286         resizeFlag = true;
287         window->width = width;
288         window->height = height;
289     }
290 
291     window->MoveWindow(window);
292     if (resizeFlag) {
293         cairo_xlib_surface_set_size(window->xlibSurface,
294                                     window->width,
295                                     window->height);
296         XResizeWindow(window->owner->dpy,
297                       window->wId,
298                       window->width,
299                       window->height);
300     }
301 
302     cairo_t* xlibc = cairo_create(window->xlibSurface);
303     cairo_set_operator(xlibc, CAIRO_OPERATOR_SOURCE);
304     cairo_set_source_surface(xlibc, window->contentSurface, 0, 0);
305     cairo_rectangle(xlibc, 0, 0, window->width, window->height);
306     cairo_clip(xlibc);
307     cairo_paint(xlibc);
308     cairo_destroy(xlibc);
309     cairo_surface_flush(window->xlibSurface);
310 }
311 
FcitxXlibWindowDestroy(FcitxXlibWindow * window)312 void FcitxXlibWindowDestroy(FcitxXlibWindow* window)
313 {
314     if (window->wId == None)
315         return;
316 
317     cairo_surface_destroy(window->contentSurface);
318     cairo_surface_destroy(window->backgroundSurface);
319     cairo_surface_destroy(window->xlibSurface);
320     XDestroyWindow(window->owner->dpy, window->wId);
321     window->wId = None;
322 }
323