Forum: PC-Programmierung X11/Xlib: Expose-Event wird 10x hintereinander ausgefuehrt


von Juergen (Gast)


Lesenswert?

Hallo,
ich habe ein simples X11-Programm unter Linux geschrieben.
Ist das Fenster verdeckt und wird dann voll sichtbar, wird es etwa 10x 
ganz schnell hintereinander gezeichnet.
Das kann nicht Sinn der Sache sein.
1
while (1)
2
{
3
4
  // pa ist ein Pointer auf eine Struktur, die wichtige IDs
5
  //  enthaelt (Windows-Handles, GC, ...)
6
  XNextEvent(pa->dis, &event);
7
8
  switch  (event.type)
9
  {
10
11
  case ButtonPress:
12
    ...
13
    break;
14
15
  case Expose:
16
17
    XGetWindowAttributes(pa->dis,pa->win,&attr);
18
    draw(pa,attr.width,attr.height);
19
    break;
20
21
  case KeyPress:
22
23
    if (XK_q == XLookupKeysym(&event.xkey, 0))
24
    {
25
26
      exit(EXIT_SUCCESS);
27
28
    }
29
30
    break;
31
32
  //case ResizeRequest:
33
34
  default:
35
36
    fprintf(stderr,"eventtype %d\n",event.type);
37
38
  }
39
40
}

Bei draw wird ziemlich viel gemalt, was ein bisschen dauert (weniger als 
1/2 Millisek.).
Ich kann mir das so erklaeren, dass nach dem 1. Expose-Event es zu lange 
dauert, bis draw() fertig ist und XFlush(dis); aufruft. In der 
Zwischenzeit schickt der X-Server weitere Expose-Events, weil der denkt, 
der 1. ist nicht angekommen.
Aber wie sollte man das anders machen? Es gibt halt viel zu zeichnen.
Andere Erklaerungen?
Gruss, Juergen

von Rolf Magnus (Gast)


Lesenswert?

Juergen schrieb:

> Andere Erklaerungen?

Wie machst du das Fenster den sichtbar? Schiebst du ein z.B. anderes 
davor weg?  Dann gibt es natürlich mehrere Expose-Events, weil ja nach 
und nach immer mehr vom Fenster sichtbar wird.
Übrigens:  Woher weiß dein Draw eigentlich, welcher Teil vom Fenster 
gerde sichtbar geworden ist? Oder zeichnet das jedesmal alles komplett 
neu, auch wenn nur zwei Pixel mehr sichtbar geworden sind?

> Aber wie sollte man das anders machen? Es gibt halt viel zu zeichnen.

Double buffering. Außerdem kannst du, wenn schon mehrere Expose-Events 
in der Queue stecken, die erstmal aalle sammeln und dann nur einmal draw 
aufrufen.

von Juergen (Gast)


Lesenswert?

>Wie machst du das Fenster den sichtbar?

Ich betaetige ALT-TAB. (Damit kann man alle Fenster der Reihe nach in 
den Vordergrund bringen. Das mache ich schon seit Windows3.1 so :-) )
Es wird auf einen Schlag sichtbar und ich zeichne alles neu.

von Andreas F. (aferber)


Lesenswert?

Schonmal mit einem anderen Windowmanager ausprobiert?

Andreas

von Rolf Magnus (Gast)


Lesenswert?

Juergen schrieb:
>>Wie machst du das Fenster den sichtbar?
>
> Ich betaetige ALT-TAB. (Damit kann man alle Fenster der Reihe nach in
> den Vordergrund bringen. Das mache ich schon seit Windows3.1 so :-) )
> Es wird auf einen Schlag sichtbar und ich zeichne alles neu.

Worauf ich hinauswollte, war, daß es mehrere Expose-Events geben kann, 
wenn Teile des Fensters erst später sichtbar werden. Das wäre z.B. auch 
der Fall, wenn dein Windowmanager bei Alt+Tab ein Popup-Fenster 
aufmacht, das erst kurz nach dem Hervorholen deines Fensters 
verschwindet und damit einen Teil davon freisetzt. Wenn's daran nicht 
liegt, kann ich mir auch nur noch den Window-Manager vorstellen. Der 
X-Server sendet keine wiederholten Expose-Events, nur weil den Programm 
noch nicht darauf reagiert hat.
Du kannst dir auch mal die Inhalte des Events ausgeben (also die 
Bereiche, die da angegeben sind).
Hast du eigentlich compsoite an? Vielleicht requestet da der 
composite-Manager das Fenster nicht am Stück, sondern in Blöcken oder 
so.

Aber wie gesagt:  Wenn das Zeichnen lange dauert, mach double buffering, 
und du hast keinen Ärger damit.

von Juergen (Gast)


Lesenswert?

>Schonmal mit einem anderen Windowmanager ausprobiert?

Mit gtk/gdk kenne ich mich zwar aus, aber fuer diese App will ich mich 
auf einen kleinen WM beschraenken, da es fuer den EEEPC 4GB gedacht ist, 
und da habe ich keine grossen HD-Resourcen mehr frei.

von Juergen (Gast)


Lesenswert?

>Du kannst dir auch mal die Inhalte des Events ausgeben (also die
>Bereiche, die da angegeben sind).


1
  case Expose:
2
3
    printf("Redrawing from Expose, (%d,%d) (%d,%d).\n",
4
      event.xexpose.x,event.xexpose.y,
5
      event.xexpose.width,event.xexpose.height);
6
7
8
9
Redrawing from Expose, (293,0) (689,176).
10
Redrawing from Expose, (293,176) (77,113).
11
Redrawing from Expose, (646,176) (336,113).
12
Redrawing from Expose, (293,289) (689,73).
13
Redrawing from Expose, (370,176) (276,113).

Tatsaechlich. Es sind verschiedene Bereiche angeben.
(Es sind hier auch nur 5 Expose Events.)
Vielleicht registriert Xlib gar nicht, dass ich beim ersten Expose 
bereits das ganze Window gezeichnet habe und denkt, ich haette nur den 
im event angebenen Auschnitt gezeichnet.

von Rolf Magnus (Gast)


Lesenswert?

Juergen schrieb:
>>Schonmal mit einem anderen Windowmanager ausprobiert?
>
> Mit gtk/gdk kenne ich mich zwar aus, aber fuer diese App will ich mich
> auf einen kleinen WM beschraenken, da es fuer den EEEPC 4GB gedacht ist,
> und da habe ich keine grossen HD-Resourcen mehr frei.

gtk ist ein API, kein Windowmanager. Und bist du sicher, daß auf dem 
EEE-PC nicht eh schon gtk drauf ist?

Juergen schrieb:
> Tatsaechlich. Es sind verschiedene Bereiche angeben.
> (Es sind hier auch nur 5 Expose Events.)
> Vielleicht registriert Xlib gar nicht, dass ich beim ersten Expose
> bereits das ganze Window gezeichnet habe und denkt, ich haette nur den
> im event angebenen Auschnitt gezeichnet.

Xlib "denkt" da gar nix. Sie gibt dir nur die Informationen, wenn Teile 
des Fensters exponiert werden. Was du als Reaktion darauf dann tust, ist 
deine Sache.

von Juergen (Gast)


Lesenswert?

>Xlib "denkt" da gar nix.
Ist mir schon klar.
Aber ich braeuchte die Moeglichkeit, dass der X-Server mir nur einen 
expose schickt unabhaengig von den aufgedeckten Fensterflaechen. D.h. 
selbst wenn nur 2 kleine Miniflaechen neugezeichnet werden muessen, soll 
er mir nur einen expose schicken fuer das ganze Fenster. (anstatt 2 
expose fuer die jeweiligen Miniflaechen)

>gtk ist ein API, kein Windowmanager. Und bist du sicher, daß auf dem
>EEE-PC nicht eh schon gtk drauf ist?
Es soll auf dem EEEPC auch modifiziert werden koennen. Die GCC-Suite ist 
schon drauf, aber ich brauechte noch libgtk2.0-dev mit den ganzen 
Abhaengigkeiten. Schaetz ich mal auf 12-20 MB (Installed Size).
Und da wird es eng.

von Rolf Magnus (Gast)


Lesenswert?

Juergen schrieb:
>>Xlib "denkt" da gar nix.
> Ist mir schon klar.
> Aber ich braeuchte die Moeglichkeit, dass der X-Server mir nur einen
> expose schickt unabhaengig von den aufgedeckten Fensterflaechen. D.h.
> selbst wenn nur 2 kleine Miniflaechen neugezeichnet werden muessen, soll
> er mir nur einen expose schicken fuer das ganze Fenster. (anstatt 2
> expose fuer die jeweiligen Miniflaechen)

Da wüßte ich keinen Weg. Ich kann da nur nochmals double buffering 
empfehlen. Damit macht es dann nichts weiter aus, wenn mehrere 
Anforderungen ankommen. Das ist auch allegmein sinnvoll. Deshalb macht 
z.B. Qt seit Version 4 grundsätzlich bei allen Widgets double buffering. 
Auch bei direkter Benutzung der Xlib sollte das recht einfach umsetzbar 
sein.

> Es soll auf dem EEEPC auch modifiziert werden koennen. Die GCC-Suite ist
> schon drauf, aber ich brauechte noch libgtk2.0-dev mit den ganzen
> Abhaengigkeiten. Schaetz ich mal auf 12-20 MB (Installed Size).
> Und da wird es eng.

Ja, ok, wenn du die ganzen dev-Pakete brauchst, ist der Platzbedarf 
natürlich erheblich größer.

von Juergen (Gast)


Lesenswert?

>Da wüßte ich keinen Weg. Ich kann da nur nochmals double buffering
>empfehlen.

double buffering ist auf jeden Fall eine gute Loesung.
Ich denke auch daran, beim Empfang eines Expose-Event einen Timer zu 
starten, der z.B. 10ms nachher die Flaechen neu zeichnet.
Kommen mehrere Expose-Events danach, wird der Timer nicht neu gestartet.

von Andreas F. (aferber)


Lesenswert?

Die XExposeEvent-Struct hat ein Feld count, dieses ist 0, wenn es sich 
um das letzte Expose-Event in einer derartigen Folge von 
zusammenhängenden Events handelt, und ungleich 0 bei den anderen.

Andreas

von Juergen (Gast)


Lesenswert?

Super. Das hat einen Erfolg gebracht.
Es wird beim obigen Szenerario jetzt nur 2x mal gezeichnet.
Die Count-Werte bei den 5 Expose-Events haben die Werte 3,2,1,0,0

Das ist ein enorme Verbesserung, wenn auch nicht das Optimum.
Danke.

von Andreas F. (aferber)


Lesenswert?

Juergen schrieb:
> Die Count-Werte bei den 5 Expose-Events haben die Werte 3,2,1,0,0

Wenn man sich die Areas genau ansieht, wird bei den ersten 4 Events ein 
Rechteck mitten im Fenster ausgespart, das dann mit dem 5. Event 
gezeichnet wird. Wahrscheinlich wirklich ein WM-Popup, halt mal die 
Alt-Taste länger fest, dann siehst du das vermutlich auch. Insofern also 
völlig korrektes Verhalten, das Rechteck in der Mitte wird eben wirklich 
erst später als der Rest dargestellt, und deshalb auch nicht in die 
Sequenz einbezogen (bzw. ohne die Aussparung gäbe es dann wohl auch nur 
ein Event).

Andreas

von User (Gast)


Lesenswert?

Evtl. beim ersten Event einen Timer starten und erst 20 oder so 
Millisekunden danach das Fenster zeichnen. Die Events dazwischen drin 
ignorieren.

von Andreas F. (aferber)


Lesenswert?

Was auch noch eine schnelle Lösung mit vielleicht schon grossem Effekt 
sein könnte ist die Aktivierung von Backing Store für das Fenster, dazu 
muss nur ein Attribut gesetzt werden. Damit hängt es dann vom X-Server 
ab. ob er verdeckte Fensterbereiche selbst buffert, moderne X-Server 
sollten das aber eigentlich machen, solange der dazu nötige 
Speicherbedarf nicht zu groß wird.

http://tronche.com/gui/x/xlib/window/attributes/backing-store.html

Andreas

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Juergen schrieb:
> Bei draw wird ziemlich viel gemalt, was ein bisschen dauert (weniger als
> 1/2 Millisek.).
Wenn es tatsächlich nur 1/2 ms dauert das neuzuzeichnen, dann ist es 
doch nicht schlimm dies 5x hintereinander zu tun ;)

von Juergen (Gast)


Lesenswert?

Ich halte 1/2 ms fuer ziemlich lange bei einem PC.
Vielleicht sind es auch 2ms, war nur geschaetzt. Man kann das Flickern 
beim Neu-zeichnen deutlich bemerken.
Und das Flickern ist das, was stoert.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Naja gegen Flakern hilft wie schon gesagt nur Double-Buffering. Ich hab 
teilweise "Zeichen-Zeiten" von an die 20 - 80 ms und da flackert nix.

von Andreas F. (aferber)


Lesenswert?

Juergen schrieb:
> Ich halte 1/2 ms fuer ziemlich lange bei einem PC.
> Vielleicht sind es auch 2ms, war nur geschaetzt. Man kann das Flickern
> beim Neu-zeichnen deutlich bemerken.

Dann dauert das Zeichnen aber erheblich länger als 2ms. 2ms 
entsprächen 500Hz, alleine schon ein einzelner Durchgang bei der 
Darstellung auf dem Monitor dauert mit 16,7ms (60Hz bei TFT angenommen) 
mehr als achtmal so lange.

Andreas

von Rolf Magnus (Gast)


Lesenswert?

Juergen schrieb:
>>Da wüßte ich keinen Weg. Ich kann da nur nochmals double buffering
>>empfehlen.
>
> double buffering ist auf jeden Fall eine gute Loesung.

Dazu müßtest du eigentlich nur eine X-Pixmap anlegen und dein draw() da 
reinrendern lassen statt ins Fenster, dann im Event-Handler nur noch die 
freigelegten Teile des Fensters aus der Pixmap rüber-"blitten" und draw 
nur noch da aufrufen, wo sich der Inhalt ändert.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.