Mercurial > projects > dynamin
comparison dynamin/gui/x_window.d @ 33:329ce1001936
Refactor painting on X by using PaintQueue.
author | Jordan Miner <jminer7@gmail.com> |
---|---|
date | Sat, 25 Jul 2009 22:49:17 -0500 |
parents | b48d3f2f570d |
children | e4d290aaa7ed |
comparison
equal
deleted
inserted
replaced
32:b48d3f2f570d | 33:329ce1001936 |
---|---|
160 Stdout("warning: WM does not support _NET_FRAME_EXTENTS").newline; | 160 Stdout("warning: WM does not support _NET_FRAME_EXTENTS").newline; |
161 if(!isWMPropertySupported(XA._NET_WORKAREA)) | 161 if(!isWMPropertySupported(XA._NET_WORKAREA)) |
162 Stdout("warning: WM does not support _NET_WORKAREA").newline; | 162 Stdout("warning: WM does not support _NET_WORKAREA").newline; |
163 } | 163 } |
164 | 164 |
165 struct _InvalidRect { | 165 struct InvalidRect { |
166 XWindow window; | 166 Window window; |
167 int x, y, width, height; | 167 int x, y, width, height; |
168 _InvalidRect getUnion(_InvalidRect rect) { | |
169 auto x2 = min(x, rect.x); | |
170 auto y2 = min(y, rect.y); | |
171 _InvalidRect rect2; | |
172 rect2.window = window; | |
173 rect2.width = max(x+width, rect.x+rect.width)-x2; | |
174 rect2.height = max(y+height, rect.y+rect.height)-y2; | |
175 rect2.x = x2; | |
176 rect2.y = y2; | |
177 return rect2; | |
178 } | |
179 } | |
180 /+struct InvalidRect { | |
181 XWindow window; | |
182 int x, y, width, height; | |
183 } | 168 } |
184 | 169 |
185 static class PaintQueue { | 170 static class PaintQueue { |
171 import tango.util.container.LinkedList; | |
172 import dynamin.painting.graphics; | |
173 import dynamin.gui.events; | |
186 static: | 174 static: |
187 //LinkedList!(InvalidRect) rects; | 175 LinkedList!(InvalidRect) rects; |
188 InvalidRect[] rects; | |
189 static this() { | 176 static this() { |
190 //rects = new LinkedList!(InvalidRect)(20); | 177 rects = new LinkedList!(InvalidRect)(); |
191 rects.length = 20; | 178 } |
192 rects.length = 0; | 179 void add(Window win, int x, int y, int width, int height) { |
193 } | 180 auto iter = rects.iterator; |
194 void add(XWindow win, int x, int y, int width, int height) { | 181 InvalidRect* r; |
195 rects.length = rects.length + 1; | 182 while((r = iter.next()) !is null) { |
196 rects[$-1].window = win; | 183 if(r.window == win && shouldMerge(r.x, r.y, r.width, r.height, |
197 rects[$-1].x = x; | 184 x, y, width, height)) { |
198 rects[$-1].y = y; | 185 join(r.x, r.y, r.width, r.height, |
199 rects[$-1].width = width; | 186 x, y, width, height); |
200 rects[$-1].height = height; | 187 return; |
188 } | |
189 } | |
190 InvalidRect rect; | |
191 rect.window = win; | |
192 rect.x = x; | |
193 rect.y = y; | |
194 rect.width = width; | |
195 rect.height = height; | |
196 rects.add(rect); | |
201 } | 197 } |
202 bool shouldMerge(int x1, int y1, int width1, int height1, | 198 bool shouldMerge(int x1, int y1, int width1, int height1, |
203 int x2, int y2, int width2, int height2) { | 199 int x2, int y2, int width2, int height2) { |
204 return x2 <= x1 + width1 && y2 <= y1 + height1 && | 200 return x2 <= x1 + width1 && y2 <= y1 + height1 && |
205 x2 + width2 >= x1 && y2 + height2 >= y1; | 201 x2 + width2 >= x1 && y2 + height2 >= y1; |
206 } | 202 } |
207 void Union(inout int x, inout int y, inout int width, inout int height, | 203 void join(inout int x, inout int y, inout int width, inout int height, |
208 int x2, int y2, int width2, int height2) { | 204 int x2, int y2, int width2, int height2) { |
209 } | 205 auto minx = min(x, x2); |
210 void compress() { | 206 auto miny = min(y, y2); |
211 } | 207 width = max(x+width, x2+width2)-minx; |
212 }+/ | 208 height = max(y+height, y2+height2)-miny; |
209 x = minx; | |
210 y = miny; | |
211 } | |
212 void paint() { | |
213 if(rects.size == 0) | |
214 return; | |
215 auto iter = rects.iterator; | |
216 InvalidRect* r; | |
217 while((r = iter.next()) !is null) | |
218 paint(r); | |
219 rects.clear(); | |
220 } | |
221 void paint(InvalidRect* rect) { | |
222 auto surfaceWin = cairo_xlib_surface_create( | |
223 display, rect.window.handle, | |
224 XDefaultVisual(display, XDefaultScreen(display)), | |
225 cast(int)rect.window.width, cast(int)rect.window.height); | |
226 // TODO: ^ should be contentWidth/height or got from evWindow | |
227 auto crWin = cairo_create(surfaceWin); | |
228 cairo_surface_destroy(surfaceWin); | |
229 | |
230 auto surfaceBuff = cairo_surface_create_similar(surfaceWin, CAIRO_CONTENT_COLOR, rect.width, rect.height); | |
231 auto crBuff = cairo_create(surfaceBuff); | |
232 cairo_translate(crBuff, | |
233 -rect.x-rect.window.borderSize.left, | |
234 -rect.y-rect.window.borderSize.top); | |
235 cairo_surface_destroy(surfaceBuff); | |
236 | |
237 cairo_set_source_rgb(crBuff, rect.window.content.backColor.R/255.0, rect.window.content.backColor.G/255.0, rect.window.content.backColor.B/255.0); | |
238 cairo_paint(crBuff); | |
239 | |
240 cairo_set_source_rgb(crBuff, 0, 0, 0); | |
241 cairo_set_line_width(crBuff, 1.0); | |
242 | |
243 auto g = new Graphics(crBuff); | |
244 scope args = new PaintingEventArgs(g); | |
245 rect.window.painting(args); | |
246 delete g; | |
247 | |
248 cairo_set_source_surface(crWin, surfaceBuff, rect.x, rect.y); | |
249 cairo_rectangle(crWin, rect.x, rect.y, rect.width, rect.height); | |
250 cairo_fill(crWin); | |
251 | |
252 cairo_destroy(crBuff); | |
253 cairo_destroy(crWin); | |
254 } | |
255 } | |
213 | 256 |
214 Key prevKey = Key.None; | 257 Key prevKey = Key.None; |
215 | 258 |
216 //{{{ ApplicationBackend | 259 //{{{ ApplicationBackend |
217 template ApplicationBackend() { | 260 template ApplicationBackend() { |
220 if(w is null) return true; | 263 if(w is null) return true; |
221 return w.visible; | 264 return w.visible; |
222 } | 265 } |
223 XEvent ev; | 266 XEvent ev; |
224 while(isWindowVisible()) { | 267 while(isWindowVisible()) { |
225 if(XEventsQueued(display, QueuedAlready) == 0) { | 268 if(XEventsQueued(display, QueuedAlready) == 0) |
226 _InvalidRect[] rects = Window.invalidRects; | 269 PaintQueue.paint(); |
227 Window.invalidRects.length = 0; | |
228 while(rects.length > 0) { | |
229 auto rect = rects[0]; | |
230 // TODO: fix this...right now it gens one Expose with | |
231 // the union of invalid rects | |
232 for(int i = rects.length-1; i >= 0; --i) { | |
233 if(rect.window == rects[i].window) { | |
234 rect = rect.getUnion(rects[i]); | |
235 | |
236 arrayCopy!(_InvalidRect)(rects, i+1, rects, i, rects.length-i-1); | |
237 rects.length = rects.length-1; | |
238 } | |
239 } | |
240 ev.xexpose.type = Expose; | |
241 ev.xexpose.display = display; | |
242 ev.xexpose.window = rect.window; | |
243 ev.xexpose.x = rect.x; | |
244 ev.xexpose.y = rect.y; | |
245 ev.xexpose.width = rect.width; | |
246 ev.xexpose.height = rect.height; | |
247 ev.xexpose.count = -2; // came from here | |
248 XPutBackEvent(display, &ev); | |
249 } | |
250 } | |
251 XNextEvent(display, &ev); | 270 XNextEvent(display, &ev); |
252 auto evDisplay = ev.xany.display; | 271 auto evDisplay = ev.xany.display; |
253 auto evWindow = ev.xany.window; | 272 auto evWindow = ev.xany.window; |
254 Window c = getControl(evWindow); | 273 Window c = getControl(evWindow); |
255 // c will be null for SelectionRequest events | 274 // c will be null for SelectionRequest events |
277 switch(ev.type) { | 296 switch(ev.type) { |
278 case MapNotify: | 297 case MapNotify: |
279 c.mapped = true; | 298 c.mapped = true; |
280 break; | 299 break; |
281 case UnmapNotify: | 300 case UnmapNotify: |
282 _InvalidRect[] rects = Window.invalidRects; | |
283 for(int i = rects.length-1; i >= 0; --i) { | |
284 if(rects[i].window == evWindow) { | |
285 arrayCopy!(_InvalidRect)( | |
286 rects, i+1, rects, i, rects.length-i-1); | |
287 rects.length = rects.length-1; | |
288 } | |
289 } | |
290 Window.invalidRects = rects; | |
291 c.mapped = false; | 301 c.mapped = false; |
292 break; | 302 break; |
293 case DestroyNotify: | 303 case DestroyNotify: |
294 setControl(evWindow, null); | 304 setControl(evWindow, null); |
295 break; | 305 break; |
374 case FocusIn: | 384 case FocusIn: |
375 break; | 385 break; |
376 case FocusOut: | 386 case FocusOut: |
377 break; | 387 break; |
378 case Expose: | 388 case Expose: |
379 // TODO: move the painting code out of here and: | 389 auto exposeEv = &ev.xexpose; |
380 // make a PaintQueue class and put this here: | 390 PaintQueue.add(c, exposeEv.x, exposeEv.y, exposeEv.width, exposeEv.height); |
381 // PaintQueue.add(c, exposeEv.x, exposeEv.y, exposeEv.width, exposeEv.height); | |
382 // then, in Window.repaint(), have this: | |
383 // PaintQueue.add(this, cast(int)x, cast(int)exposeEv.y, cast(int)exposeEv.width, cast(int)exposeEv.height); | |
384 // Have a PaintQueue.Compress method that merges | |
385 // all invalidated rects that touch or overlap. | |
386 // In the if(!XPending(..)) above, just loop over all the | |
387 // rects in the PaintQueue, painting them. | |
388 auto exposeEv = ev.xexpose; | |
389 if(exposeEv.count != -2) { | |
390 c.repaint(exposeEv.x, exposeEv.y, exposeEv.width, exposeEv.height); | |
391 break; | |
392 } | |
393 //printf("repainting x=%d, y=%d, width=%d, height=%d\n", | |
394 // exposeEv.x, exposeEv.y, exposeEv.width, exposeEv.height); | |
395 | |
396 auto surfaceWin = cairo_xlib_surface_create( | |
397 evDisplay, evWindow, | |
398 XDefaultVisual(evDisplay, XDefaultScreen(evDisplay)), | |
399 cast(int)c.width, cast(int)c.height); | |
400 // TODO: ^ should be contentWidth/height or got from evWindow | |
401 auto crWin = cairo_create(surfaceWin); | |
402 cairo_surface_destroy(surfaceWin); | |
403 | |
404 auto surfaceBuff = cairo_surface_create_similar(surfaceWin, CAIRO_CONTENT_COLOR, exposeEv.width, exposeEv.height); | |
405 // TODO: use cairo_translate instead, I guess, as | |
406 // I had to change the Windows backend to it... | |
407 cairo_surface_set_device_offset(surfaceBuff, -exposeEv.x-c._borderSize.left, -exposeEv.y-c._borderSize.top); | |
408 auto crBuff = cairo_create(surfaceBuff); | |
409 cairo_surface_destroy(surfaceBuff); | |
410 | |
411 cairo_set_source_rgb(crBuff, w.content.backColor.R/255.0, w.content.backColor.G/255.0, w.content.backColor.B/255.0); | |
412 cairo_paint(crBuff); | |
413 | |
414 cairo_set_source_rgb(crBuff, 0, 0, 0); | |
415 cairo_set_line_width(crBuff, 1.0); | |
416 | |
417 auto g = new Graphics(crBuff); | |
418 scope args = new PaintingEventArgs(g); | |
419 c.painting(args); | |
420 delete g; | |
421 | |
422 cairo_surface_set_device_offset(surfaceBuff, -exposeEv.x, -exposeEv.y); | |
423 cairo_set_source_surface(crWin, surfaceBuff, 0, 0); | |
424 cairo_rectangle(crWin, exposeEv.x, exposeEv.y, exposeEv.width, exposeEv.height); | |
425 cairo_fill(crWin); | |
426 | |
427 cairo_destroy(crBuff); | |
428 cairo_destroy(crWin); | |
429 break; | 391 break; |
430 case PropertyNotify: | 392 case PropertyNotify: |
431 auto propertyEv = ev.xproperty; | 393 auto propertyEv = ev.xproperty; |
432 if(propertyEv.atom == XA._NET_FRAME_EXTENTS && | 394 if(propertyEv.atom == XA._NET_FRAME_EXTENTS && |
433 propertyEv.state != PropertyDelete) | 395 propertyEv.state != PropertyDelete) |
723 } | 685 } |
724 void backend_setCurrentCursor(Cursor cur) { | 686 void backend_setCurrentCursor(Cursor cur) { |
725 XDefineCursor(display, _handle, cur.handle); | 687 XDefineCursor(display, _handle, cur.handle); |
726 } | 688 } |
727 | 689 |
728 static _InvalidRect[] invalidRects; | |
729 void backend_repaint(Rect rect) { | 690 void backend_repaint(Rect rect) { |
730 invalidRects.length = invalidRects.length+1; | 691 PaintQueue.add(this, |
731 invalidRects[$-1].window = _handle; | 692 cast(int)(rect.x-borderSize.left), cast(int)(rect.y-borderSize.top), |
732 invalidRects[$-1].x = cast(int)(rect.x-borderSize.left); | 693 cast(int)rect.width+1, cast(int)rect.height+1); |
733 invalidRects[$-1].y = cast(int)(rect.y-borderSize.top); | 694 //Stdout.format("invalidating x={}, y={}, width={}, height={}", rect.x, rect.y, rect.width, rect.height).newline; |
734 invalidRects[$-1].width = cast(int)rect.width+1; | |
735 invalidRects[$-1].height = cast(int)rect.height+1; | |
736 //printf("invalidating x=%.1f, y=%.1f, width=%.1f, height=%.1f\n", rect.X, rect.Y, rect.width, rect.height); | |
737 } | 695 } |
738 void backend_resizable(bool b) { | 696 void backend_resizable(bool b) { |
739 backend_updateWM_NORMAL_HINTS(); | 697 backend_updateWM_NORMAL_HINTS(); |
740 } | 698 } |
741 void backend_contentMinSizeChanged() { | 699 void backend_contentMinSizeChanged() { |