Back to index

texmacs  1.0.7.15
x_loop.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : x_loop.cpp
00004 * DESCRIPTION: The main event loop
00005 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
00006 *******************************************************************************
00007 * This software falls under the GNU general public license version 3 or later.
00008 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00009 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00010 ******************************************************************************/
00011 
00012 #include "X11/x_gui.hpp"
00013 #include "X11/x_window.hpp"
00014 #include "iterator.hpp"
00015 #include "converter.hpp"
00016 #include "tm_link.hpp"
00017 #include "message.hpp"
00018 
00019 #ifdef OS_WIN32
00020 #include <sys/time.h>
00021 #include <sys/misc.h>
00022 #include <sys/_types.h>
00023 #else
00024 #include <sys/time.h>
00025 #include <sys/types.h>
00026 #include <unistd.h>
00027 #endif
00028 
00029 #if defined(X11TEXMACS) && defined(MACOSX_EXTENSIONS)
00030 #include "MacOS/mac_app.h"
00031 #endif
00032 
00033 extern hashmap<Window,pointer> Window_to_window;
00034 int  nr_windows= 0;
00035 int  alt_mask= 0;
00036 int  meta_mask= 0;
00037 
00038 static int  kbd_count= 0;
00039 static bool request_partial_redraw= false;
00040 
00041 /******************************************************************************
00042 * Hack for getting the remote time
00043 ******************************************************************************/
00044 
00045 static bool   time_initialized= false;
00046 static time_t time_difference = 0;
00047 
00048 static void
00049 synchronize_time (Time t) {
00050   if (time_initialized && time_difference == 0) return;
00051   time_t d= texmacs_time () - ((time_t) t);
00052   if (time_initialized) {
00053     if (d < time_difference)
00054       time_difference= d;
00055   }
00056   else {
00057     time_initialized= true;
00058     time_difference = d;
00059   }
00060   if (-1000 <= time_difference && time_difference <= 1000)
00061     time_difference= 0;
00062 }
00063 
00064 static time_t
00065 remote_time (Time t) {
00066   return ((time_t) t) + time_difference;
00067 }
00068 
00069 /******************************************************************************
00070 * Look up keys and mouse
00071 ******************************************************************************/
00072 
00073 string
00074 x_gui_rep::look_up_key (XKeyEvent* ev) {
00075   KeySym key= 0;
00076   //cout << ev->state << ", " << ev->keycode << LF;
00077 
00078   if (im_ok) {
00079     x_window win= (x_window) Window_to_window[ev->window];
00080     char str[256];
00081     Status status;
00082     int count = Xutf8LookupString (win->ic, (XKeyPressedEvent*) ev,
00083                                str, 256, &key, &status);
00084     string r (str, count);
00085     r= utf8_to_cork (r);
00086     if (contains_unicode_char (r)) return r;
00087   }
00088   else XLookupString (ev, NULL, 0, &key, NULL);
00089   string s= ((ev->state&1)? upper_key [key]: lower_key [key]);
00090   if ((N(s)>=2) && (s[0]=='K') && (s[1]=='-')) s= s (2, N(s));
00091 
00092   /* other keyboard modifiers */
00093   if (N(s)==0) return s;
00094   if (ev->state & 4) s= "C-" * s;
00095   if (ev->state & alt_mask) s= "A-" * s;
00096   if (ev->state & meta_mask) s= "M-" * s;
00097   // cout << "key press: " << s << LF;
00098   return s;
00099 }
00100 
00101 string
00102 x_gui_rep::look_up_mouse (XButtonEvent* ev) {
00103   switch (ev->button) {
00104   case Button1: return "left";
00105   case Button2: return "middle";
00106   case Button3: return "right";
00107   case Button4: return "up";
00108   case Button5: return "down";
00109   default: return "unknown";
00110   }
00111 }
00112 
00113 unsigned int
00114 x_gui_rep::get_button_mask (XButtonEvent* ev) {
00115   switch (ev->button) {
00116   case Button1: return Button1Mask;
00117   case Button2: return Button2Mask;
00118   case Button3: return Button3Mask;
00119   case Button4: return Button4Mask;
00120   case Button5: return Button5Mask;
00121   default: return 0;
00122   }
00123 }
00124 
00125 /******************************************************************************
00126 * Process events
00127 ******************************************************************************/
00128 
00129 typedef const char* charp;
00130 
00131 charp event_name[]= {
00132   "?",
00133   "?",
00134   "Key press",
00135   "Key release",
00136   "Button press",
00137   "Button release",
00138   "Motion notify",
00139   "Enter notify",
00140   "Leave notify",
00141   "Focus in",
00142   "Focus out",
00143   "Keymap notify",
00144   "Expose",
00145   "Graphics expose",
00146   "No expose",
00147   "Visibility notify",
00148   "Create notify",
00149   "Destroy notify",
00150   "Unmap notify",
00151   "Map request",
00152   "Reparent notify",
00153   "Configure notify",
00154   "Configure request",
00155   "Gravity notify",
00156   "Resize request",
00157   "Circulate notify",
00158   "Circulate request",
00159   "Property notify",
00160   "Selection clear",
00161   "Selection request",
00162   "Selection notify",
00163   "Colormap notify",
00164   "Client message",
00165   "Mapping notify"
00166 };
00167 
00168 void
00169 x_gui_rep::process_event (x_window win, XEvent* ev) {
00170   //if (ev->type != NoExpose)
00171   //cout << "Event: " << event_name[ev->type] << "\n";
00172   switch (ev->type) {
00173   case Expose:
00174     {
00175       XExposeEvent& ee= ev->xexpose;
00176       /*
00177       cout << "Expose: " << ee.x << "," << ee.y << ","
00178            << ee.x+ee.width << "," << ee.y+ee.height << "\n";
00179       */
00180       win->invalidate_event (ee.x, ee.y, ee.x+ee.width, ee.y+ee.height);
00181       break;
00182     }
00183   case GraphicsExpose:
00184     {
00185       XGraphicsExposeEvent& ee= ev->xgraphicsexpose;
00186       /*
00187       cout << "Expose: " << ee.x << "," << ee.y << ","
00188           << ee.x+ee.width << "," << ee.y+ee.height << "\n";
00189           */
00190       win->invalidate_event (ee.x, ee.y, ee.x+ee.width, ee.y+ee.height);
00191       break;
00192     }
00193   case NoExpose:
00194     // cout << "No expose\n";
00195     break;
00196   case ConfigureNotify:
00197     /*
00198     cout << "Configure move  : " << ev->xconfigure.x << ","
00199         << ev->xconfigure.y << "\n";
00200     cout << "Configure resize: " << ev->xconfigure.width << ","
00201         << ev->xconfigure.height << "\n";
00202         */
00203     /*
00204     if ((ev->xconfigure.x!=0) || (ev->xconfigure.y!=0) ||
00205        ((ev->xconfigure.width == win->win_w) &&
00206         (ev->xconfigure.height == win->win_h)))
00207     */
00208     win->move_event (ev->xconfigure.x, ev->xconfigure.y);
00209     win->resize_event (ev->xconfigure.width, ev->xconfigure.height);
00210     break;
00211   case CreateNotify:
00212     break;
00213   case DestroyNotify:
00214     // cout << "Destroy\n";
00215     // win->destroy_event ();
00216     // event_loop ();
00217     // exit (0);
00218     break;
00219   case UnmapNotify:
00220     // cout << "Unmap\n";
00221     break;
00222   case ButtonPress:
00223     unmap_balloon ();
00224     set_button_state (ev->xbutton.state ^ get_button_mask (&ev->xbutton));
00225     win->mouse_event ("press-" * look_up_mouse (&ev->xbutton),
00226                     ev->xbutton.x, ev->xbutton.y, ev->xbutton.time);
00227     break;
00228   case ButtonRelease:
00229     unmap_balloon ();
00230     set_button_state (ev->xbutton.state ^ get_button_mask (&ev->xbutton));
00231     win->mouse_event ("release-" * look_up_mouse (&ev->xbutton),
00232                     ev->xbutton.x, ev->xbutton.y, ev->xbutton.time);
00233     break;
00234   case EnterNotify:
00235     unmap_balloon ();
00236     if (ev->xcrossing.mode == NotifyNormal) {
00237       // cout << "Enter at (" <<ev->xcrossing.x<<","<<ev->xcrossing.y << ")\n";
00238       set_button_state (ev->xcrossing.state);
00239       win->mouse_event ("enter", ev->xcrossing.x, ev->xcrossing.y,
00240                      ev->xcrossing.time);
00241     }
00242     break;
00243   case LeaveNotify:
00244     unmap_balloon ();
00245     if (ev->xcrossing.mode == NotifyNormal) {
00246       // cout << "Leave at (" <<ev->xcrossing.x<<","<<ev->xcrossing.y << ")\n";
00247       set_button_state (ev->xcrossing.state);
00248       win->mouse_event ("leave", ev->xcrossing.x, ev->xcrossing.y,
00249                      ev->xcrossing.time);
00250     }
00251     break;
00252   case FocusIn:
00253     win->focus_in_event ();
00254     break;
00255   case FocusOut:
00256     win->focus_out_event ();
00257     break;
00258   case MotionNotify:
00259     // cout << "Move to (" << ev->xmotion.x << "," << ev->xmotion.y << ")\n";
00260     unmap_balloon ();
00261     set_button_state (ev->xmotion.state);
00262     win->mouse_event ("move", ev->xmotion.x, ev->xmotion.y, ev->xmotion.time);
00263     break;
00264   case KeyPress:
00265     unmap_balloon ();
00266     {
00267       string key= look_up_key (&ev->xkey);
00268       //cout << "Press " << key << " at " << (time_t) ev->xkey.time
00269       //<< " (" << texmacs_time() << ")\n";
00270       kbd_count++;
00271       synchronize_time (ev->xkey.time);
00272       if (texmacs_time () - remote_time (ev->xkey.time) < 100 ||
00273          (kbd_count & 15) == 0)
00274        request_partial_redraw= true;
00275       //cout << "key   : " << key << "\n";
00276       //cout << "redraw: " << request_partial_redraw << "\n";
00277       if (N(key)>0) win->key_event (key);
00278       break;
00279     }
00280   case SelectionRequest: {
00281       XSelectionRequestEvent& req= ev->xselectionrequest;
00282       XSelectionEvent sel;
00283       sel.type      = SelectionNotify;
00284       sel.requestor = req.requestor;
00285       sel.selection = req.selection;
00286       sel.target    = req.target;
00287       sel.time      = req.time;
00288       string key = "none";
00289       if (req.selection == XA_PRIMARY) key = "mouse";
00290       else if (req.selection == XA_CLIPBOARD) key = "primary";
00291       if (!selection_s->contains (key)) sel.property= None;
00292       else if (req.target==XA_TARGETS) {
00293         Atom targets[2];
00294         targets[0] = XA_TARGETS;
00295         targets[1] = XA_STRING;
00296         XChangeProperty (dpy, req.requestor, req.property, XA_ATOM,
00297                          32, PropModeReplace,
00298                          (unsigned char*)&targets[0],2);
00299         sel.property  = req.property;
00300       }
00301       else if ((req.target==AnyPropertyType) || (req.target==XA_STRING)) {
00302         char *txt = as_charp (selection_s(key));
00303         XChangeProperty (dpy, req.requestor, req.property, XA_STRING,
00304                          8, PropModeReplace,
00305                          (unsigned char*) txt,
00306                          strlen (txt));
00307         tm_delete_array (txt);
00308         sel.property  = req.property;
00309       }
00310       else sel.property = None;
00311       XSendEvent (dpy, sel.requestor, false, 0, (XEvent*) &sel);
00312     }
00313     break;
00314   case SelectionClear:
00315     {
00316       XSelectionClearEvent& req= ev->xselectionclear;
00317       if (selection_w == req.window) selection_w= (Window) 0;
00318       else if (req.selection == XA_PRIMARY) clear_selection ("mouse");
00319       else if (req.selection == XA_CLIPBOARD) clear_selection ("primary");
00320     }
00321     break;
00322   case ClientMessage:
00323     {
00324       Atom wm_protocols     = XInternAtom (win->dpy, "WM_PROTOCOLS",     1);
00325       Atom wm_delete_window = XInternAtom (win->dpy, "WM_DELETE_WINDOW", 1);
00326       if ((ev->xclient.message_type == wm_protocols) &&
00327          ((Atom)ev->xclient.data.l[0] == wm_delete_window))
00328        win->destroy_event();
00329       break;
00330     }
00331   }
00332 }
00333 
00334 /******************************************************************************
00335 * Main event loop
00336 ******************************************************************************/
00337 
00338 static void (*the_interpose_handler) (void) = NULL;
00339 void gui_interpose (void (*r) (void)) { the_interpose_handler= r; }
00340 
00341 #define MIN_DELAY   10
00342 #define MAX_DELAY   1000
00343 #define SLEEP_AFTER 120000
00344 
00345 void
00346 x_gui_rep::event_loop () {
00347   bool wait  = true;
00348   int count  = 0;
00349   int delay  = MIN_DELAY;
00350 
00351   while (nr_windows>0 || number_of_servers () != 0) {
00352     request_partial_redraw= false;
00353 
00354     // Get events
00355     XEvent report;
00356     if (XPending (dpy) > 0) {
00357       XNextEvent (dpy, &report);
00358       if (XFilterEvent (&report, (Window) NULL) == True) continue;
00359       //if (string (event_name[report.type]) != "No expose")
00360       //cout << "Event: " << event_name[report.type] << "\n";
00361       x_window win= (x_window) Window_to_window[report.xany.window];
00362       if (win!=NULL) process_event (win, &report);
00363       count= 0;
00364       delay= MIN_DELAY;
00365       wait = false;
00366     }
00367     if (nr_windows == 0) continue;
00368 
00369     // Don't typeset when resizing window
00370     if (XPending (dpy) > 0)
00371       if (report.type == ConfigureNotify ||
00372          report.type == Expose ||
00373          report.type == NoExpose) continue;
00374 
00375     // Wait for events on all channels and interpose
00376     //time_t t1= texmacs_time ();
00377     if (wait) {
00378       struct timeval tv;
00379       tv.tv_sec  = delay/1000;
00380       tv.tv_usec = 1000 * (delay%1000);
00381       select (0, NULL, NULL, NULL, &tv);
00382       count += delay;
00383       if (count >= SLEEP_AFTER) delay= MAX_DELAY;
00384     }
00385     else wait= true;
00386     if (the_interpose_handler != NULL) the_interpose_handler ();
00387     if (nr_windows == 0) continue;
00388     //time_t t2= texmacs_time ();
00389     //if (t2 - t1 >= 10) cout << "interpose took " << t2-t1 << "ms\n";
00390 
00391     // Popup help balloons
00392     if (!is_nil (balloon_wid))
00393       if (texmacs_time () - balloon_time >= 666)
00394        if (balloon_win == NULL)
00395          map_balloon ();
00396 
00397     // Redraw invalid windows
00398     //time_t t3= texmacs_time ();
00399     if (XPending (dpy) == 0 || request_partial_redraw) {
00400       interrupted= false;
00401       interrupt_time= texmacs_time () + (100 / (XPending (dpy) + 1));
00402       iterator<Window> it= iterate (Window_to_window);
00403       while (it->busy()) { // first the window which has the focus
00404        x_window win= (x_window) Window_to_window[it->next()];
00405        if (win->has_focus) win->repaint_invalid_regions();
00406       }
00407       it= iterate (Window_to_window);
00408       while (it->busy()) { // and then the other windows
00409        x_window win= (x_window) Window_to_window[it->next()];
00410        if (!win->has_focus) win->repaint_invalid_regions();
00411       }
00412     }
00413     //time_t t4= texmacs_time ();
00414     //if (t4 - t3 >= 10) cout << "redraw took " << t4-t3 << "ms\n";
00415 
00416     // Handle alarm messages
00417     if (!is_nil (messages)) {
00418       list<message> not_ready;
00419       while (!is_nil (messages)) {
00420        time_t ct= texmacs_time ();
00421        message m= messages->item;
00422        if ((m->t - ct) <= 0) send_delayed_message (m->wid, m->s, m->t);
00423        else not_ready= list<message> (m, not_ready);
00424        messages= messages->next;
00425       }
00426       messages= not_ready;
00427     }
00428     
00429 #if defined(X11TEXMACS) && defined (MACOSX_EXTENSIONS)
00430     process_mac_events ();
00431 #endif
00432     
00433     
00434   }
00435 }