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