2016-12-26 4 views
1

J'écris une application sous Linux en utilisant Xlib pour gérer une fenêtre et cairo pour dessiner un texte en elle. Le contenu du texte de la fenêtre change pendant l'exécution, donc je veux adapter la taille de la fenêtre à celle de l'extension du texte. Si la taille de l'extension de texte ne change pas, la fenêtre est toujours correctement mise à jour avec le nouveau texte.Après XResizeWindow, le nouveau contenu de la fenêtre tirée ne s'affiche

Mais lorsque l'étendue du texte change et que la fenêtre est redimensionnée en conséquence, la fenêtre est effacée mais le nouveau texte n'est jamais affiché. Seulement s'il n'y a aucun appel à XResizeWindow le texte est réellement affiché. Le code que je utilise est

if (/* Text extent is changed */) 
{ 
    XResizeWindow (display, window, new_width, new_height); 
    cairo_xlib_surface_set_size (surface, new_width, new_height); 
} 

XClearWindow (display, window); 

/* ... Cairo code to draw the text ... */ 

// cairo_surface_flush (surface); 
// XFlush (display); 

J'ai aussi essayé d'ajouter après le code du Caire qui attire le texte les méthodes cairo_surface_flush et XFlush (commenté dans l'exemple), mais rien ne change.

EDIT: je résolu le problème en utilisant deux fils: le premier fil avec la boucle habituelle pour écouter les événements Expose plus le code pour redessiner le contenu et le second fil qui délivre le redimensionnement de la fenêtre et envoie un événement Expose pour réveiller le premier thread.

Dans cet exemple, la fenêtre est redimensionnée toutes les 500 ms à une largeur et une hauteur aléatoires et un compteur progressif y est affiché à chaque redimensionnement. J'utilise C++ 11, compiler avec:

g++ -std=c++11 -o test test.cpp -lX11 -lcairo -lpthread 

Le code est:

#include <random> 
#include <chrono> 
#include <thread> 
#include <string> 
#include <X11/Xlib.h> 
#include <cairo/cairo-xlib.h> 

Display * d; 
Window w; 
cairo_surface_t * surface; 
int width = 300, height = 300; 
unsigned char counter = 0; 
std::random_device rd; 
std::knuth_b gen (rd()); 
std::uniform_int_distribution < > dist (150, 300); 

void logic() 
{ 
    XEvent send_event; 
    send_event.type = Expose; 
    send_event.xexpose.window = w; 

    while (true) 
    { 
     std::this_thread::sleep_for (std::chrono::milliseconds (500)); 
     ++ counter; 
     width = dist (gen); 
     height = dist (gen); 
     cairo_xlib_surface_set_size (surface, width, height); 
     XResizeWindow (d, w, width, height); 
     XSendEvent (d, w, False, ExposureMask, & send_event); 
     XFlush (d); 
    } 
} 

int main () 
{ 
    XInitThreads(); 

    d = XOpenDisplay (NULL); 
    w = XCreateSimpleWindow (d, RootWindow (d, 0), 0, 0, width, height, 0, 0, 0x000000); 
    XMapWindow (d, w); 
    XSelectInput (d, w, ExposureMask | KeyPressMask); 

    surface = cairo_xlib_surface_create (d, w, DefaultVisual (d, 0), width, height); 
    cairo_t * cairo = cairo_create (surface); 
    cairo_select_font_face (cairo, "FreeSans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); 
    cairo_set_font_size (cairo, 40); 
    cairo_set_source_rgb (cairo, 0.8, 0.8, 0.8); 
    cairo_move_to (cairo, 40.0, 60.0); 
    cairo_show_text (cairo, std::to_string (counter).c_str()); 
    XFlush (d); 

    std::thread T (logic); 

    XEvent event; 
    while (true) 
    { 
     XNextEvent (d, & event); 
     if (event.type == Expose) 
     { 
      XClearWindow (d, w); 
      cairo_move_to (cairo, 40.0, 60.0); 
      cairo_show_text (cairo, std::to_string (counter).c_str()); 
     } 
     else if (event.type == KeyPress) 
     { 
      XCloseDisplay (d); 
      return 0; 
     } 
    } 
} 

Mais une question reste: est-il possible d'obtenir le même résultat en utilisant seulement un fil?

+0

Avez-vous un exemple autonome que l'on pourrait tester? À quoi ressemble votre boucle d'événements, c'est-à-dire que faites-vous sur les événements Expose? –

+1

Ok Je vais fournir un exemple de code autonome compact qui reproduit le comportement. – disquisitiones

Répondre

1

Voici une version monothread de votre code. Ce n'est pas bien, mais ça semble marcher. La partie la plus difficile attend les événements du serveur X11 et le timeout simultanément. Je fais cela avec select() dans le code suivant. Notez que je gère également ConfigureNotify événements au lieu de supposer que XResizeWindow fait toujours ce que nous voulons.

#include <random> 
#include <chrono> 
#include <thread> 
#include <string> 
#include <X11/Xlib.h> 
#include <cairo/cairo-xlib.h> 
#include <sys/time.h> 

Display * d; 
Window w; 
cairo_surface_t * surface; 
int width = 300, height = 300; 
unsigned char counter = 0; 
std::random_device rd; 
std::knuth_b gen (rd()); 
std::uniform_int_distribution < > dist (150, 300); 

void do_update() 
{ 
     ++ counter; 
     width = dist (gen); 
     height = dist (gen); 
     XResizeWindow (d, w, width, height); 
     // Force a redraw 
     XClearArea(d, w, 0, 0, 0, 0, True); 
} 

int main () 
{ 
    XInitThreads(); 

    d = XOpenDisplay (NULL); 
    w = XCreateSimpleWindow (d, RootWindow (d, 0), 0, 0, width, height, 0, 0, 0x000000); 
    XMapWindow (d, w); 
    XSelectInput (d, w, ExposureMask | KeyPressMask); 

    surface = cairo_xlib_surface_create (d, w, DefaultVisual (d, 0), width, height); 
    cairo_t * cairo = cairo_create (surface); 
    cairo_select_font_face (cairo, "FreeSans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); 
    cairo_set_font_size (cairo, 40); 
    cairo_set_source_rgb (cairo, 0.8, 0.8, 0.8); 
    cairo_move_to (cairo, 40.0, 60.0); 
    cairo_show_text (cairo, std::to_string (counter).c_str()); 
    XFlush (d); 

    struct timeval next_update; 
    struct timeval now; 
    struct timeval interval = { 0, 500000 }; 
    gettimeofday(&now, NULL); 
    timeradd(&now, &interval, &next_update); 

    while (true) 
    { 
     XEvent event; 

     gettimeofday(&now, NULL); 
     if (timercmp(&now, &next_update, >)) { 
      // Store time of next update 
      timeradd(&now, &interval, &next_update); 
      puts("update"); 
      do_update(); 
     } 

     if (!XPending(d)) { 
      struct timeval remaining; 
      fd_set fds; 
      int fd = ConnectionNumber(d); 
      FD_ZERO(&fds); 
      FD_SET(fd, &fds); 
      timersub(&next_update, &now, &remaining); 
      select(fd + 1, &fds, NULL, NULL, &remaining); 
     } else { 
      XNextEvent (d, & event); 
      if (event.type == Expose) 
      { 
       XClearWindow (d, w); 
       cairo_move_to (cairo, 40.0, 60.0); 
       cairo_show_text (cairo, std::to_string (counter).c_str()); 
      } 
      if (event.type == ConfigureNotify) 
      { 
       cairo_xlib_surface_set_size (surface, event.xconfigure.width, event.xconfigure.height); 
      } 
      else if (event.type == KeyPress) 
      { 
       XCloseDisplay (d); 
       return 0; 
      } 
     } 
    } 
} 
+0

Le code est assez intéressant, merci. Quelques questions: 1) Est-ce que la fonction ** select ** est utilisée juste pour attendre et implémenter le timer ou y a-t-il aussi quelque chose lié à l'attente de lectures non bloquantes sur le descripteur de fichier de l'affichage? 2) La fonction ClearArea utilisée dans update() est-elle utilisée uniquement pour déclencher l'envoi d'un événement Expose depuis le serveur? – disquisitiones

+0

1) Bien ... select() est utilisé pour "attendre jusqu'à ce que le temps se soit écoulé, mais gérer tous les événements du serveur immédiatement, le cas échéant". 2) Oui. 3) Notez que je n'ai pas optimisé ce code de quelque façon que ce soit. Il doit appeler cairo_surface_flush() après avoir dessiné. Il devrait seulement gérer les événements d'exposition dont l'entrée de compte est zéro etc. Probablement beaucoup d'autres problèmes. –

+0

Ok, j'ai compris ... Mais il me reste un doute sur le fonctionnement du serveur X, et c'est en fait à l'origine de ma question. Je ne comprends pas pourquoi, même après que le client appelle XFlush ou XSync, le serveur n'effectue pas le dessin réel sur la fenêtre.Il semble que seulement si le client lit dans la boucle habituelle les événements Expose avec XNextEvent, le serveur honorera réellement les demandes de dessin de client. Pourquoi est-ce? Le serveur vérifie-t-il si le client a lu l'événement Expose, puis exécute le dessin réel? – disquisitiones