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() {