Back to index

easystroke  0.5.5.1
main.cc
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008-2009, Thomas Jaeger <ThJaeger@gmail.com>
00003  *
00004  * Permission to use, copy, modify, and/or distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
00009  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00010  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
00011  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00012  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
00013  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
00014  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 #include "win.h"
00017 #include "main.h"
00018 #include "shape.h"
00019 #include "prefs.h"
00020 #include "actiondb.h"
00021 #include "prefdb.h"
00022 #include "trace.h"
00023 #include "annotate.h"
00024 #include "fire.h"
00025 #include "water.h"
00026 #include "composite.h"
00027 #include "grabber.h"
00028 
00029 #include <glibmm/i18n.h>
00030 
00031 #include <X11/Xutil.h>
00032 #include <X11/extensions/XTest.h>
00033 #include <X11/extensions/Xfixes.h>
00034 #include <X11/Xproto.h>
00035 // From #include <X11/extensions/XIproto.h>
00036 // which is not C++-safe
00037 #define X_GrabDeviceButton              17
00038 
00039 #include <string.h>
00040 #include <signal.h>
00041 #include <fcntl.h>
00042 #include <getopt.h>
00043 
00044 extern Source<bool> disabled;
00045 
00046 bool experimental = false;
00047 int verbosity = 0;
00048 const char *prefs_versions[] = { "-0.5.5", "-0.4.1", "-0.4.0", "", NULL };
00049 const char *actions_versions[] = { "-0.4.1", "-0.4.0", "", NULL };
00050 Source<Window> current_app_window(None);
00051 std::string config_dir;
00052 Win *win = NULL;
00053 Display *dpy;
00054 Window ROOT;
00055 bool in_proximity = false;
00056 boost::shared_ptr<sigc::slot<void, RStroke> > stroke_action;
00057 Grabber::XiDevice *current_dev = 0;
00058 std::set<guint> xinput_pressed; // TODO get rid of
00059 
00060 static bool show_gui = false;
00061 static bool no_dbus = false;
00062 
00063 static int argc;
00064 static char **argv;
00065 
00066 static Window ping_window = 0;
00067 static boost::shared_ptr<Trace> trace;
00068 static std::map<guint, guint> core_inv_map;
00069 
00070 class Handler;
00071 static Handler *handler = 0;
00072 static ActionDBWatcher *action_watcher = 0;
00073 
00074 static int (*oldHandler)(Display *, XErrorEvent *) = 0;
00075 static int (*oldIOHandler)(Display *) = 0;
00076 
00077 static Trace *trace_composite() {
00078        try {
00079               return new Composite();
00080        } catch (std::exception &e) {
00081               if (verbosity >= 1)
00082                      printf("Falling back to Shape method: %s\n", e.what());
00083               return new Shape();
00084        }
00085 }
00086 
00087 static Trace *init_trace() {
00088        try {
00089               switch(prefs.trace.get()) {
00090                      case TraceNone:
00091                             return new Trivial();
00092                      case TraceShape:
00093                             return new Shape();
00094                      case TraceAnnotate:
00095                             return new Annotate();
00096                      case TraceFire:
00097                             return new Fire();
00098                      case TraceWater:
00099                             return new Water();
00100                      default:
00101                             return trace_composite();
00102               }
00103        } catch (DBusException &e) {
00104               printf(_("Error: %s\n"), e.what());
00105               return trace_composite();
00106        }
00107 
00108 }
00109 
00110 void fake_core_button(guint b, bool press) {
00111        if (core_inv_map.count(b))
00112               b = core_inv_map[b];
00113        XTestFakeButtonEvent(dpy, b, press, CurrentTime);
00114 }
00115 
00116 static void fake_click(guint b) {
00117        fake_core_button(b, true);
00118        fake_core_button(b, false);
00119 }
00120 
00121 static std::list<sigc::slot<void> > queued;
00122 
00123 class Handler {
00124 protected:
00125        Handler *child;
00126 public:
00127        Handler *parent;
00128        Handler() : child(0), parent(0) {}
00129        Handler *top() {
00130               if (child)
00131                      return child->top();
00132               else
00133                      return this;
00134        }
00135        static bool idle() { return !handler->child; }
00136 
00137        virtual void motion(RTriple e) {}
00138        virtual void raw_motion(RTriple e, bool, bool) {}
00139        virtual void press(guint b, RTriple e) {}
00140        virtual void release(guint b, RTriple e) {}
00141        virtual void press_master(guint b, Time t) {}
00142        virtual void pressure() {}
00143        virtual void proximity_out() {}
00144        virtual void pong() {}
00145        void replace_child(Handler *c) {
00146               if (child)
00147                      delete child;
00148               child = c;
00149               if (child)
00150                      child->parent = this;
00151               if (verbosity >= 2) {
00152                      std::string stack;
00153                      for (Handler *h = child ? child : this; h; h=h->parent) {
00154                             stack = h->name() + " " + stack;
00155                      }
00156                      printf("New event handling stack: %s\n", stack.c_str());
00157               }
00158               Handler *new_handler = child ? child : this;
00159               grabber->grab(new_handler->grab_mode());
00160               if (child)
00161                      child->init();
00162               while (queued.size() && Handler::idle()) {
00163                      (*queued.begin())();
00164                      queued.pop_front();
00165               }
00166        }
00167        virtual void init() {}
00168        virtual ~Handler() {
00169               if (child)
00170                      delete child;
00171        }
00172        virtual std::string name() = 0;
00173        virtual Grabber::State grab_mode() = 0;
00174 };
00175 
00176 void queue(sigc::slot<void> f) {
00177        if (Handler::idle()) {
00178               f();
00179               XFlush(dpy);
00180        } else
00181               queued.push_back(f);
00182 }
00183 
00184 class OSD : public Gtk::Window {
00185        static std::list<OSD *> osd_stack;
00186        int w;
00187 public:
00188        OSD(Glib::ustring txt) : Gtk::Window(Gtk::WINDOW_POPUP) {
00189               osd_stack.push_back(this);
00190               set_accept_focus(false);
00191               set_border_width(15);
00192               WIDGET(Gtk::Label, label, "<big><b>" + txt + "</b></big>");
00193               label.set_use_markup();
00194               label.modify_fg(Gtk::STATE_NORMAL, Gdk::Color("White"));
00195               modify_bg(Gtk::STATE_NORMAL, Gdk::Color("RoyalBlue3"));
00196               set_opacity(0.75);
00197               add(label);
00198               label.show();
00199               int h;
00200               get_size(w,h);
00201               do_move();
00202               show();
00203               get_window()->input_shape_combine_region(Gdk::Region(), 0, 0);
00204        }
00205        static void do_move() {
00206               int left = gdk_screen_width() - 10;
00207               for (std::list<OSD *>::iterator i = osd_stack.begin(); i != osd_stack.end(); i++) {
00208                      left -= (*i)->w + 30;
00209                      (*i)->move(left, 40);
00210               }
00211        }
00212        virtual ~OSD() {
00213               osd_stack.remove(this);
00214               do_move();
00215        }
00216 };
00217 
00218 std::list<OSD *> OSD::osd_stack;
00219 
00220 class IgnoreHandler : public Handler {
00221        RModifiers mods;
00222 public:
00223        IgnoreHandler(RModifiers mods_) : mods(mods_) {}
00224        virtual void press(guint b, RTriple e) {
00225               if (current_dev->master) {
00226                      XTestFakeMotionEvent(dpy, DefaultScreen(dpy), e->x, e->y, 0);
00227                      XTestFakeButtonEvent(dpy, b, true, CurrentTime);
00228               }
00229        }
00230        virtual void motion(RTriple e) {
00231               if (current_dev->master)
00232                      XTestFakeMotionEvent(dpy, DefaultScreen(dpy), e->x, e->y, 0);
00233        }
00234        // TODO: Handle Proximity
00235        virtual void release(guint b, RTriple e) {
00236               if (current_dev->master) {
00237                      XTestFakeMotionEvent(dpy, DefaultScreen(dpy), e->x, e->y, 0);
00238                      XTestFakeButtonEvent(dpy, b, false, CurrentTime);
00239               }
00240               if (!xinput_pressed.size())
00241                      parent->replace_child(NULL);
00242        }
00243        virtual std::string name() { return "Ignore"; }
00244        virtual Grabber::State grab_mode() { return Grabber::NONE; }
00245 };
00246 
00247 class ButtonHandler : public Handler {
00248        RModifiers mods;
00249        guint button, real_button;
00250 public:
00251        ButtonHandler(RModifiers mods_, guint button_) : mods(mods_), button(button_), real_button(0) {}
00252        virtual void press(guint b, RTriple e) {
00253               if (current_dev->master) {
00254                      if (!real_button)
00255                             real_button = b;
00256                      if (real_button == b)
00257                             b = button;
00258                      XTestFakeMotionEvent(dpy, DefaultScreen(dpy), e->x, e->y, 0);
00259                      XTestFakeButtonEvent(dpy, b, true, CurrentTime);
00260               }
00261        }
00262        virtual void motion(RTriple e) {
00263               if (current_dev->master)
00264                      XTestFakeMotionEvent(dpy, DefaultScreen(dpy), e->x, e->y, 0);
00265        }
00266        // TODO: Handle Proximity
00267        virtual void release(guint b, RTriple e) {
00268               if (current_dev->master) {
00269                      if (real_button == b)
00270                             b = button;
00271                      XTestFakeMotionEvent(dpy, DefaultScreen(dpy), e->x, e->y, 0);
00272                      XTestFakeButtonEvent(dpy, b, false, CurrentTime);
00273               }
00274               if (!xinput_pressed.size())
00275                      parent->replace_child(NULL);
00276        }
00277        virtual std::string name() { return "Button"; }
00278        virtual Grabber::State grab_mode() { return Grabber::NONE; }
00279 };
00280 
00281 static void bail_out();
00282 
00283 static void bail_out() {
00284        handler->replace_child(NULL);
00285        xinput_pressed.clear();
00286        XFlush(dpy);
00287 }
00288 
00289 static int xErrorHandler(Display *dpy2, XErrorEvent *e) {
00290        if (dpy != dpy2)
00291               return oldHandler(dpy2, e);
00292        if (verbosity == 0 && e->error_code == BadWindow) {
00293               switch (e->request_code) {
00294                      case X_ChangeWindowAttributes:
00295                      case X_GetProperty:
00296                      case X_QueryTree:
00297                             return 0;
00298               }
00299        }
00300        if (e->request_code == X_GrabButton ||
00301                      (grabber && e->request_code == grabber->event &&
00302                       e->minor_code == X_GrabDeviceButton)) {
00303               if (!handler || Handler::idle()) {
00304                      printf(_("Error: %s\n"), e->request_code==X_GrabButton ? "A grab failed" : "An XInput grab failed");
00305               } else {
00306                      printf(_("Error: A grab failed.  Resetting...\n"));
00307                      bail_out();
00308               }
00309        } else {
00310               char text[64];
00311               XGetErrorText(dpy, e->error_code, text, sizeof text);
00312               char msg[16];
00313               snprintf(msg, sizeof msg, "%d", e->request_code);
00314               char def[32];
00315               snprintf(def, sizeof def, "request_code=%d, minor_code=%d", e->request_code, e->minor_code);
00316               char dbtext[128];
00317               XGetErrorDatabaseText(dpy, "XRequest", msg,
00318                             def, dbtext, sizeof dbtext);
00319               printf("XError: %s: %s\n", text, dbtext);
00320        }
00321        return 0;
00322 }
00323 
00324 static int xIOErrorHandler(Display *dpy2) {
00325        if (dpy != dpy2)
00326               return oldIOHandler(dpy2);
00327        printf(_("Fatal Error: Connection to X server lost, restarting...\n"));
00328        char *args[argc+1];
00329        for (int i = 0; i<argc; i++)
00330               args[i] = argv[i];
00331        args[argc] = NULL;
00332        execv(argv[0], args);
00333        return 0;
00334 }
00335 
00336 static XAtom EASYSTROKE_PING("EASYSTROKE_PING");
00337 static void ping() {
00338        XClientMessageEvent ev;
00339        ev.type = ClientMessage;
00340        ev.window = ping_window;
00341        ev.message_type = *EASYSTROKE_PING;
00342        ev.format = 32;
00343        XSendEvent(dpy, ping_window, False, 0, (XEvent *)&ev);
00344        XFlush(dpy);
00345 }
00346 
00347 class WaitForPongHandler : public Handler, protected Timeout {
00348 public:
00349        WaitForPongHandler() { set_timeout(100); }
00350        virtual void timeout() {
00351               printf("Warning: %s timed out\n", "WaitForPongHandler");
00352               bail_out();
00353        }
00354        virtual void pong() { parent->replace_child(NULL); }
00355        virtual std::string name() { return "WaitForPong"; }
00356        virtual Grabber::State grab_mode() { return parent->grab_mode(); }
00357 };
00358 
00359 static void update_core_mapping() {
00360        unsigned char map[MAX_BUTTONS];
00361        int n = XGetPointerMapping(dpy, map, MAX_BUTTONS);
00362        core_inv_map.clear();
00363        for (int i = n-1; i; i--)
00364               if (map[i] == i+1)
00365                      core_inv_map.erase(i+1);
00366               else
00367                      core_inv_map[map[i]] = i+1;
00368 }
00369 
00370 static inline float abs(float x) { return x > 0 ? x : -x; }
00371 
00372 class AbstractScrollHandler : public Handler {
00373        bool have_x, have_y;
00374        float last_x, last_y;
00375        Time last_t;
00376        float offset_x, offset_y;
00377        Glib::ustring str;
00378        int orig_x, orig_y;
00379 
00380 protected:
00381        AbstractScrollHandler() : last_t(0), offset_x(0.0), offset_y(0.0) {
00382               if (!prefs.move_back.get() || current_dev->absolute)
00383                      return;
00384               Window dummy1, dummy2;
00385               int dummy3, dummy4;
00386               unsigned int dummy5;
00387               XQueryPointer(dpy, ROOT, &dummy1, &dummy2, &orig_x, &orig_y, &dummy3, &dummy4, &dummy5);
00388        }
00389        virtual void fake_wheel(int b1, int n1, int b2, int n2) {
00390               for (int i = 0; i<n1; i++)
00391                      fake_click(b1);
00392               for (int i = 0; i<n2; i++)
00393                      fake_click(b2);
00394        }
00395        static float curve(float v) {
00396               return v * exp(log(abs(v))/3);
00397        }
00398 protected:
00399        void move_back() {
00400               if (!prefs.move_back.get() || current_dev->absolute)
00401                      return;
00402               XTestFakeMotionEvent(dpy, DefaultScreen(dpy), orig_x, orig_y, 0);
00403        }
00404 public:
00405        virtual void raw_motion(RTriple e, bool abs_x, bool abs_y) {
00406               float dx = abs_x ? (have_x ? e->x - last_x : 0) : e->x;
00407               float dy = abs_y ? (have_y ? e->y - last_y : 0) : e->y;
00408 
00409               if (abs_x) {
00410                      last_x = e->x;
00411                      have_x = true;
00412               }
00413 
00414               if (abs_y) {
00415                      last_y = e->y;
00416                      have_y = true;
00417               }
00418 
00419               if (!last_t) {
00420                      last_t = e->t;
00421                      return;
00422               }
00423 
00424               if (e->t == last_t)
00425                      return;
00426 
00427               int dt = e->t - last_t;
00428               last_t = e->t;
00429 
00430               double factor = (prefs.scroll_invert.get() ? 1.0 : -1.0) * prefs.scroll_speed.get();
00431               offset_x += factor * curve(dx/dt)*dt/20.0;
00432               offset_y += factor * curve(dy/dt)*dt/10.0;
00433               int b1 = 0, n1 = 0, b2 = 0, n2 = 0;
00434               if (abs(offset_x) > 1.0) {
00435                      n1 = (int)floor(abs(offset_x));
00436                      if (offset_x > 0) {
00437                             b1 = 7;
00438                             offset_x -= n1;
00439                      } else {
00440                             b1 = 6;
00441                             offset_x += n1;
00442                      }
00443               }
00444               if (abs(offset_y) > 1.0) {
00445                      if (abs(offset_y) < 1.0)
00446                             return;
00447                      n2 = (int)floor(abs(offset_y));
00448                      if (offset_y > 0) {
00449                             b2 = 5;
00450                             offset_y -= n2;
00451                      } else {
00452                             b2 = 4;
00453                             offset_y += n2;
00454                      }
00455               }
00456               if (n1 || n2)
00457                      fake_wheel(b1,n1, b2,n2);
00458        }
00459 };
00460 
00461 class ScrollHandler : public AbstractScrollHandler {
00462        RModifiers mods;
00463        std::map<guint, guint> map;
00464        int orig_x, orig_y;
00465 public:
00466        ScrollHandler(RModifiers mods_) : mods(mods_) {
00467               // If you want to use buttons > 9, you're on your own..
00468               map[1] = 0; map[2] = 0; map[3] = 0; map[8] = 0; map[9] = 0;
00469        }
00470        virtual void raw_motion(RTriple e, bool abs_x, bool abs_y) {
00471               if (xinput_pressed.size())
00472                      AbstractScrollHandler::raw_motion(e, abs_x, abs_y);
00473        }
00474        virtual void press_master(guint b, Time t) {
00475               fake_core_button(b, false);
00476        }
00477        virtual void release(guint b, RTriple e) {
00478               if (in_proximity || xinput_pressed.size())
00479                      return;
00480               parent->replace_child(0);
00481               move_back();
00482        }
00483        virtual void proximity_out() {
00484               parent->replace_child(0);
00485               move_back();
00486        }
00487        virtual std::string name() { return "Scroll"; }
00488        virtual Grabber::State grab_mode() { return Grabber::RAW; }
00489 };
00490 
00491 class ScrollAdvancedHandler : public AbstractScrollHandler {
00492        RModifiers m;
00493        guint &rb;
00494 public:
00495        ScrollAdvancedHandler(RModifiers m_, guint &rb_) : m(m_), rb(rb_) {}
00496        virtual void fake_wheel(int b1, int n1, int b2, int n2) {
00497               AbstractScrollHandler::fake_wheel(b1, n1, b2, n2);
00498               rb = 0;
00499        }
00500        virtual void release(guint b, RTriple e) {
00501               Handler *p = parent;
00502               p->replace_child(NULL);
00503               p->release(b, e);
00504               move_back();
00505        }
00506        virtual void press(guint b, RTriple e) {
00507               Handler *p = parent;
00508               p->replace_child(NULL);
00509               p->press(b, e);
00510               move_back();
00511        }
00512        virtual std::string name() { return "ScrollAdvanced"; }
00513        virtual Grabber::State grab_mode() { return Grabber::RAW; }
00514 };
00515 
00516 // Hack so that we don't have to move stuff around so much
00517 static bool mods_equal(RModifiers m1, RModifiers m2);
00518 
00519 class AdvancedStrokeActionHandler : public Handler {
00520        RStroke s;
00521 public:
00522        AdvancedStrokeActionHandler(RStroke s_, RTriple e) : s(s_) {}
00523        virtual void press(guint b, RTriple e) {
00524               if (stroke_action) {
00525                      s->button = b;
00526                      (*stroke_action)(s);
00527               }
00528        }
00529        virtual void release(guint b, RTriple e) {
00530               if (stroke_action)
00531                      (*stroke_action)(s);
00532               if (xinput_pressed.size() == 0)
00533                      parent->replace_child(NULL);
00534        }
00535        virtual std::string name() { return "InstantStrokeAction"; }
00536        virtual Grabber::State grab_mode() { return Grabber::NONE; }
00537 };
00538 
00539 class AdvancedHandler : public Handler {
00540        RTriple e;
00541        guint remap_from, remap_to;
00542        Time click_time;
00543        guint replay_button;
00544        RTriple replay_orig;
00545        std::map<guint, RAction> as;
00546        std::map<guint, RRanking> rs;
00547        std::map<guint, RModifiers> mods;
00548        RModifiers sticky_mods;
00549        guint button1, button2;
00550        RPreStroke replay;
00551 
00552        void show_ranking(guint b, RTriple e) {
00553               if (!rs.count(b))
00554                      return;
00555               Ranking::queue_show(rs[b], e);
00556               rs.erase(b);
00557        }
00558        AdvancedHandler(RStroke s, RTriple e_, guint b1, guint b2, RPreStroke replay_) :
00559               e(e_), remap_from(0), remap_to(0), click_time(0), replay_button(0),
00560               button1(b1), button2(b2), replay(replay_) {
00561                      if (s)
00562                             actions.get_action_list(grabber->current_class->get())->handle_advanced(s, as, rs, b1, b2);
00563               }
00564 public:
00565        static Handler *create(RStroke s, RTriple e, guint b1, guint b2, RPreStroke replay) {
00566               if (stroke_action && s)
00567                      return new AdvancedStrokeActionHandler(s, e);
00568               else
00569                      return new AdvancedHandler(s, e, b1, b2, replay);
00570 
00571        }
00572        virtual void init() {
00573               if (replay && replay->size()) {
00574                      bool replay_first = !as.count(button2);
00575                      PreStroke::iterator i = replay->begin();
00576                      if (replay_first)
00577                             press(button2 ? button2 : button1, *i);
00578                      while (i != replay->end())
00579                             motion(*i++);
00580                      if (!replay_first)
00581                             press(button2 ? button2 : button1, e);
00582               } else {
00583                      press(button2 ? button2 : button1, e);
00584               }
00585               replay.reset();
00586        }
00587        virtual void press(guint b, RTriple e) {
00588               if (current_dev->master)
00589                      XTestFakeMotionEvent(dpy, DefaultScreen(dpy), e->x, e->y, 0);
00590               click_time = 0;
00591               if (remap_to) {
00592                      fake_core_button(remap_to, false);
00593               }
00594               remap_from = 0;
00595               remap_to = 0;
00596               replay_button = 0;
00597               guint bb = (b == button1) ? button2 : b;
00598               show_ranking(bb, e);
00599               if (!as.count(bb)) {
00600                      sticky_mods.reset();
00601                      if (current_dev->master)
00602                             XTestFakeButtonEvent(dpy, b, true, CurrentTime);
00603                      return;
00604               }
00605               RAction act = as[bb];
00606               if (IS_SCROLL(act)) {
00607                      click_time = e->t;
00608                      replay_button = b;
00609                      replay_orig = e;
00610                      RModifiers m = act->prepare();
00611                      sticky_mods.reset();
00612                      return replace_child(new ScrollAdvancedHandler(m, replay_button));
00613               }
00614               if (IS_IGNORE(act)) {
00615                      click_time = e->t;
00616                      replay_button = b;
00617                      replay_orig = e;
00618               }
00619               IF_BUTTON(act, b2) {
00620                      // This is kind of a hack:  Store modifiers in
00621                      // sticky_mods, so that they are automatically released
00622                      // on the next press
00623                      sticky_mods = act->prepare();
00624                      remap_from = b;
00625                      remap_to = b2;
00626                      fake_core_button(b2, true);
00627                      return;
00628               }
00629               mods[b] = act->prepare();
00630               if (IS_KEY(act)) {
00631                      if (mods_equal(sticky_mods, mods[b]))
00632                             mods[b] = sticky_mods;
00633                      else
00634                             sticky_mods = mods[b];
00635               } else
00636                      sticky_mods.reset();
00637               act->run();
00638        }
00639        virtual void motion(RTriple e) {
00640               if (replay_button && hypot(replay_orig->x - e->x, replay_orig->y - e->y) > 16)
00641                      replay_button = 0;
00642               if (current_dev->master)
00643                      XTestFakeMotionEvent(dpy, DefaultScreen(dpy), e->x, e->y, 0);
00644        }
00645        virtual void release(guint b, RTriple e) {
00646               if (current_dev->master)
00647                      XTestFakeMotionEvent(dpy, DefaultScreen(dpy), e->x, e->y, 0);
00648               if (remap_to) {
00649                      fake_core_button(remap_to, false);
00650               }
00651               guint bb = (b == button1) ? button2 : b;
00652               if (!as.count(bb)) {
00653                      sticky_mods.reset();
00654                      if (current_dev->master)
00655                             XTestFakeButtonEvent(dpy, b, false, CurrentTime);
00656               }
00657               if (xinput_pressed.size() == 0) {
00658                      if (e->t < click_time + 250 && b == replay_button) {
00659                             sticky_mods.reset();
00660                             mods.clear();
00661                             fake_click(b);
00662                      }
00663                      return parent->replace_child(NULL);
00664               }
00665               replay_button = 0;
00666               mods.erase((b == button1) ? button2 : b);
00667               if (remap_from)
00668                      sticky_mods.reset();
00669               remap_from = 0;
00670        }
00671        virtual std::string name() { return "Advanced"; }
00672        virtual Grabber::State grab_mode() { return Grabber::NONE; }
00673 };
00674 
00675 Atom get_atom(Window w, Atom prop) {
00676        Atom actual_type;
00677        int actual_format;
00678        unsigned long nitems, bytes_after;
00679        unsigned char *prop_return = NULL;
00680 
00681        if (XGetWindowProperty(dpy, w, prop, 0, sizeof(Atom), False, XA_ATOM, &actual_type, &actual_format,
00682                             &nitems, &bytes_after, &prop_return) != Success)
00683               return None;
00684        if (!prop_return)
00685               return None;
00686        Atom atom = *(Atom *)prop_return;
00687        XFree(prop_return);
00688        return atom;
00689 }
00690 
00691 bool has_atom(Window w, Atom prop, Atom value) {
00692        Atom actual_type;
00693        int actual_format;
00694        unsigned long nitems, bytes_after;
00695        unsigned char *prop_return = NULL;
00696 
00697        if (XGetWindowProperty(dpy, w, prop, 0, sizeof(Atom), False, XA_ATOM, &actual_type, &actual_format,
00698                             &nitems, &bytes_after, &prop_return) != Success)
00699               return None;
00700        if (!prop_return)
00701               return None;
00702        Atom *atoms = (Atom *)prop_return;
00703        bool ans = false;
00704        for (unsigned long i = 0; i < nitems; i++)
00705               if (atoms[i] == value)
00706                      ans = true;
00707        XFree(prop_return);
00708        return ans;
00709 }
00710 
00711 Window get_window(Window w, Atom prop) {
00712        Atom actual_type;
00713        int actual_format;
00714        unsigned long nitems, bytes_after;
00715        unsigned char *prop_return = NULL;
00716 
00717        if (XGetWindowProperty(dpy, w, prop, 0, sizeof(Atom), False, XA_WINDOW, &actual_type, &actual_format,
00718                             &nitems, &bytes_after, &prop_return) != Success)
00719               return None;
00720        if (!prop_return)
00721               return None;
00722        Window ret = *(Window *)prop_return;
00723        XFree(prop_return);
00724        return ret;
00725 }
00726 
00727 static void icccm_client_message(Window w, Atom a, Time t) {
00728        static XAtom WM_PROTOCOLS("WM_PROTOCOLS");
00729        XClientMessageEvent ev;
00730        ev.type = ClientMessage;
00731        ev.window = w;
00732        ev.message_type = *WM_PROTOCOLS;
00733        ev.format = 32;
00734        ev.data.l[0] = a;
00735        ev.data.l[1] = t;
00736        XSendEvent(dpy, w, False, 0, (XEvent *)&ev);
00737 }
00738 
00739 static void activate_window(Window w, Time t) {
00740        static XAtom _NET_ACTIVE_WINDOW("_NET_ACTIVE_WINDOW");
00741        static XAtom _NET_WM_WINDOW_TYPE("_NET_WM_WINDOW_TYPE");
00742        static XAtom _NET_WM_WINDOW_TYPE_DOCK("_NET_WM_WINDOW_TYPE_DOCK");
00743        static XAtom WM_PROTOCOLS("WM_PROTOCOLS");
00744        static XAtom WM_TAKE_FOCUS("WM_TAKE_FOCUS");
00745 
00746        if (w == get_window(ROOT, *_NET_ACTIVE_WINDOW))
00747               return;
00748 
00749        Atom window_type = get_atom(w, *_NET_WM_WINDOW_TYPE);
00750        if (window_type == *_NET_WM_WINDOW_TYPE_DOCK)
00751               return;
00752 
00753        XWMHints *wm_hints = XGetWMHints(dpy, w);
00754        if (wm_hints) {
00755               bool input = wm_hints->input;
00756               XFree(wm_hints);
00757               if (!input)
00758                      return;
00759        }
00760 
00761        if (!has_atom(w, *WM_PROTOCOLS, *WM_TAKE_FOCUS))
00762               return;
00763 
00764        XWindowAttributes attr;
00765        if (XGetWindowAttributes(dpy, w, &attr) && attr.override_redirect)
00766               return;
00767 
00768        if (verbosity >= 3)
00769               printf("Giving focus to window 0x%lx\n", w);
00770 
00771        icccm_client_message(w, *WM_TAKE_FOCUS, t);
00772 }
00773 
00774 void Trace::start(Trace::Point p) {
00775        last = p;
00776        active = true;
00777        XFixesHideCursor(dpy, ROOT);
00778        start_();
00779 }
00780 
00781 void Trace::end() {
00782        if (!active)
00783               return;
00784        active = false;
00785        XFixesShowCursor(dpy, ROOT);
00786        end_();
00787 }
00788 
00789 static void get_timeouts(TimeoutType type, int *init, int *final) {
00790        switch (type) {
00791               case TimeoutOff:
00792                      *init = 0;
00793                      *final = 0;
00794                      break;
00795               case TimeoutConservative:
00796                      *init = 750;
00797                      *final = 750;
00798                      break;
00799               case TimeoutDefault:
00800                      *init = 250;
00801                      *final = 250;
00802                      break;
00803               case TimeoutMedium:
00804                      *init = 100;
00805                      *final = 100;
00806                      break;
00807               case TimeoutAggressive:
00808                      *init = 50;
00809                      *final = 75;
00810                      break;
00811               case TimeoutFlick:
00812                      *init = 30;
00813                      *final = 50;
00814                      break;
00815               default:;
00816        }
00817 }
00818 
00819 class StrokeHandler : public Handler, public sigc::trackable {
00820        guint button;
00821        RPreStroke cur;
00822        bool is_gesture;
00823        bool drawing;
00824        RTriple last, orig;
00825        bool use_timeout;
00826        int init_timeout, final_timeout, radius;
00827        struct Connection {
00828               sigc::connection c;
00829               double dist;
00830               Connection(StrokeHandler *parent, double dist_, int to) : dist(dist_) {
00831                      c = Glib::signal_timeout().connect(sigc::mem_fun(*parent, &StrokeHandler::timeout), to);
00832               }
00833               ~Connection() { c.disconnect(); }
00834        };
00835        typedef boost::shared_ptr<Connection> RConnection;
00836        sigc::connection init_connection;
00837        std::vector<RConnection> connections;
00838 
00839        RStroke finish(guint b) {
00840               trace->end();
00841               XFlush(dpy);
00842               RPreStroke c = cur;
00843               if (!is_gesture || grabber->is_instant(button))
00844                      c.reset(new PreStroke);
00845               if (b && prefs.advanced_ignore.get())
00846                      c.reset(new PreStroke);
00847               return Stroke::create(*c, button, b, false);
00848        }
00849 
00850        bool timeout() {
00851               if (verbosity >= 2)
00852                      printf("Aborting stroke...\n");
00853               trace->end();
00854               RPreStroke c = cur;
00855               if (!is_gesture)
00856                      c.reset(new PreStroke);
00857               RStroke s;
00858               if (prefs.timeout_gestures.get() || grabber->is_click_hold(button))
00859                      s = Stroke::create(*c, button, 0, true);
00860               parent->replace_child(AdvancedHandler::create(s, last, button, 0, cur));
00861               XFlush(dpy);
00862               return false;
00863        }
00864 
00865        void do_instant() {
00866               PreStroke ps;
00867               RStroke s = Stroke::create(ps, button, button, false);
00868               parent->replace_child(AdvancedHandler::create(s, orig, button, button, cur));
00869        }
00870 
00871        bool expired(RConnection c, double dist) {
00872               c->dist -= dist;
00873               return c->dist < 0;
00874        }
00875 protected:
00876        void abort_stroke() {
00877               parent->replace_child(AdvancedHandler::create(RStroke(), last, button, 0, cur));
00878        }
00879        virtual void pressure() {
00880               abort_stroke();
00881               timeout();
00882        }
00883        virtual void motion(RTriple e) {
00884               cur->add(e);
00885               float dist = hypot(e->x-orig->x, e->y-orig->y);
00886               if (!is_gesture && dist > 16) {
00887                      if (use_timeout && !final_timeout)
00888                             return abort_stroke();
00889                      init_connection.disconnect();
00890                      is_gesture = true;
00891               }
00892               if (!drawing && dist > 4 && (!use_timeout || final_timeout)) {
00893                      drawing = true;
00894                      bool first = true;
00895                      for (PreStroke::iterator i = cur->begin(); i != cur->end(); i++) {
00896                             Trace::Point p;
00897                             p.x = (*i)->x;
00898                             p.y = (*i)->y;
00899                             if (first) {
00900                                    trace->start(p);
00901                                    first = false;
00902                             } else {
00903                                    trace->draw(p);
00904                             }
00905                      }
00906               } else if (drawing) {
00907                      Trace::Point p;
00908                      p.x = e->x;
00909                      p.y = e->y;
00910                      trace->draw(p);
00911               }
00912               if (use_timeout && is_gesture) {
00913                      connections.erase(remove_if(connections.begin(), connections.end(),
00914                                           sigc::bind(sigc::mem_fun(*this, &StrokeHandler::expired),
00915                                                  hypot(e->x - last->x, e->y - last->y))), connections.end());
00916                      connections.push_back(RConnection(new Connection(this, radius, final_timeout)));
00917               }
00918               last = e;
00919        }
00920 
00921        virtual void press(guint b, RTriple e) {
00922               RStroke s = finish(b);
00923               parent->replace_child(AdvancedHandler::create(s, e, button, b, cur));
00924        }
00925 
00926        virtual void release(guint b, RTriple e) {
00927               RStroke s = finish(0);
00928 
00929               if (prefs.move_back.get() && !current_dev->absolute)
00930                      XTestFakeMotionEvent(dpy, DefaultScreen(dpy), orig->x, orig->y, 0);
00931               else
00932                      XTestFakeMotionEvent(dpy, DefaultScreen(dpy), e->x, e->y, 0);
00933 
00934               if (stroke_action) {
00935                      (*stroke_action)(s);
00936                      return parent->replace_child(NULL);
00937               }
00938               RRanking ranking;
00939               RAction act = actions.get_action_list(grabber->current_class->get())->handle(s, ranking);
00940               if (!IS_CLICK(act))
00941                      Ranking::queue_show(ranking, e);
00942               if (!act) {
00943                      XBell(dpy, 0);
00944                      return parent->replace_child(NULL);
00945               }
00946               RModifiers mods = act->prepare();
00947               if (IS_CLICK(act))
00948                      act = Button::create((Gdk::ModifierType)0, b);
00949               else IF_BUTTON(act, b)
00950                      return parent->replace_child(new ButtonHandler(mods, b));
00951               if (IS_IGNORE(act))
00952                      return parent->replace_child(new IgnoreHandler(mods));
00953               if (IS_SCROLL(act))
00954                      return parent->replace_child(new ScrollHandler(mods));
00955               char buf[16];
00956               snprintf(buf, sizeof(buf), "%d", (int)orig->x);
00957               setenv("EASYSTROKE_X1", buf, 1);
00958               snprintf(buf, sizeof(buf), "%d", (int)orig->y);
00959               setenv("EASYSTROKE_Y1", buf, 1);
00960               snprintf(buf, sizeof(buf), "%d", (int)e->x);
00961               setenv("EASYSTROKE_X2", buf, 1);
00962               snprintf(buf, sizeof(buf), "%d", (int)e->y);
00963               setenv("EASYSTROKE_Y2", buf, 1);
00964               act->run();
00965               unsetenv("EASYSTROKE_X1");
00966               unsetenv("EASYSTROKE_Y1");
00967               unsetenv("EASYSTROKE_X2");
00968               unsetenv("EASYSTROKE_Y2");
00969               parent->replace_child(NULL);
00970        }
00971 public:
00972        StrokeHandler(guint b, RTriple e) : button(b), is_gesture(false), drawing(false), last(e), orig(e),
00973        init_timeout(prefs.init_timeout.get()), final_timeout(prefs.final_timeout.get()), radius(16) {
00974               const std::map<std::string, TimeoutType> &dt = prefs.device_timeout.ref();
00975               std::map<std::string, TimeoutType>::const_iterator j = dt.find(current_dev->name);
00976               if (j != dt.end())
00977                      get_timeouts(j->second, &init_timeout, &final_timeout);
00978               else
00979                      get_timeouts(prefs.timeout_profile.get(), &init_timeout, &final_timeout);
00980               use_timeout = init_timeout;
00981        }
00982        virtual void init() {
00983               if (grabber->is_instant(button))
00984                      return do_instant();
00985               if (grabber->is_click_hold(button)) {
00986                      use_timeout = true;
00987                      init_timeout = 500;
00988                      final_timeout = 0;
00989               }
00990               cur = PreStroke::create();
00991               cur->add(orig);
00992               if (!use_timeout)
00993                      return;
00994               if (final_timeout && final_timeout < 32 && radius < 16*32/final_timeout) {
00995                      radius = 16*32/final_timeout;
00996                      final_timeout = final_timeout*radius/16;
00997               }
00998               init_connection = Glib::signal_timeout().connect(
00999                             sigc::mem_fun(*this, &StrokeHandler::timeout), init_timeout);
01000        }
01001        ~StrokeHandler() { trace->end(); }
01002        virtual std::string name() { return "Stroke"; }
01003        virtual Grabber::State grab_mode() { return Grabber::NONE; }
01004 };
01005 
01006 class IdleHandler : public Handler {
01007 protected:
01008        virtual void init() {
01009               update_core_mapping();
01010        }
01011        virtual void press(guint b, RTriple e) {
01012               if (current_app_window.get())
01013                      activate_window(current_app_window.get(), e->t);
01014               replace_child(new StrokeHandler(b, e));
01015        }
01016 public:
01017        virtual ~IdleHandler() {
01018               XUngrabKey(dpy, XKeysymToKeycode(dpy,XK_Escape), AnyModifier, ROOT);
01019        }
01020        virtual std::string name() { return "Idle"; }
01021        virtual Grabber::State grab_mode() { return Grabber::BUTTON; }
01022 };
01023 
01024 class SelectHandler : public Handler {
01025        virtual void press_master(guint b, Time t) {
01026               parent->replace_child(new WaitForPongHandler);
01027               ping();
01028               queue(sigc::ptr_fun(&gtk_main_quit));
01029        }
01030 public:
01031        static void create() {
01032               win->get_window().get_window()->lower();
01033               handler->top()->replace_child(new SelectHandler);
01034        }
01035        virtual std::string name() { return "Select"; }
01036        virtual Grabber::State grab_mode() { return Grabber::SELECT; }
01037 };
01038 
01039 static void do_run_by_name(RAction act) {
01040        RModifiers mods = act->prepare();
01041        if (IS_IGNORE(act))
01042               return handler->replace_child(new IgnoreHandler(mods));
01043        if (IS_SCROLL(act))
01044               return handler->replace_child(new ScrollHandler(mods));
01045        act->run();
01046 }
01047 
01048 void run_by_name(const char *str) {
01049        if (!strcmp(str, "")) {
01050               win->show_hide();
01051               return;
01052        }
01053        for (ActionDB::const_iterator i = actions.begin(); i != actions.end(); i++) {
01054               if (i->second.name == std::string(str)) {
01055                      if (i->second.action)
01056                             queue(sigc::bind(sigc::ptr_fun(do_run_by_name), i->second.action));
01057                      return;
01058               }
01059        }
01060        printf(_("Warning: No action \"%s\" defined\n"), str);
01061 }
01062 
01063 void icon_warning() {
01064        for (ActionDB::const_iterator i = actions.begin(); i != actions.end(); i++) {
01065               Misc *m = dynamic_cast<Misc *>(i->second.action.get());
01066               if (m && m->type == Misc::SHOWHIDE)
01067                      return;
01068        }
01069        if (!win)
01070               return;
01071 
01072        Gtk::MessageDialog *md;
01073        widgets->get_widget("dialog_icon", md);
01074        md->set_message(_("Tray icon disabled"));
01075        md->set_secondary_text(_("To bring the configuration dialog up again, you should define an action of type Misc...Show/Hide."));
01076        md->run();
01077        md->hide();
01078 }
01079 
01080 void quit() {
01081        static bool dead = false;
01082        if (dead)
01083               bail_out();
01084        dead = true;
01085        queue(sigc::ptr_fun(&Gtk::Main::quit));
01086 }
01087 
01088 static void quit(int) { quit(); }
01089 
01090 class Main {
01091        std::string parse_args_and_init_gtk();
01092        void create_config_dir();
01093        char* next_event();
01094        void usage(char *me, bool good);
01095        void version();
01096 
01097        std::string display;
01098        Gtk::Main *kit;
01099 public:
01100        Main();
01101        void run();
01102        bool handle(Glib::IOCondition);
01103        void handle_enter_leave(XEvent &ev);
01104        void handle_event(XEvent &ev);
01105        void handle_xi2_event(XIDeviceEvent *event);
01106        void handle_raw_motion(XIRawEvent *event);
01107        void report_xi2_event(XIDeviceEvent *event, const char *type);
01108        ~Main();
01109 };
01110 
01111 class ReloadTrace : public Timeout {
01112        void timeout() {
01113               if (verbosity >= 2)
01114                      printf("Reloading gesture display\n");
01115               queue(sigc::mem_fun(*this, &ReloadTrace::reload));
01116        }
01117        void reload() { trace.reset(init_trace()); }
01118 } reload_trace;
01119 
01120 static void schedule_reload_trace() { reload_trace.set_timeout(1000); }
01121 
01122 static void xdg_open(const Glib::ustring str) {
01123        if (!fork()) {
01124               execlp("xdg-open", "xdg-open", str.c_str(), NULL);
01125               exit(EXIT_FAILURE);
01126        }
01127 }
01128 
01129 static void link_button_hook(Gtk::LinkButton *, const Glib::ustring& uri) { xdg_open(uri); }
01130 static void about_dialog_hook(Gtk::AboutDialog &, const Glib::ustring& url) { xdg_open(url); }
01131 
01132 // dbus-send --type=method_call --dest=org.easystroke /org/easystroke org.easystroke.send string:"foo"
01133 static void send_dbus(const char *str) {
01134        GError *error = 0;
01135        DBusGConnection *bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
01136        if (!bus) {
01137               printf(_("Error initializing D-BUS\n"));
01138               exit(EXIT_FAILURE);
01139        }
01140        DBusGProxy *proxy = dbus_g_proxy_new_for_name(bus, "org.easystroke", "/org/easystroke", "org.easystroke");
01141        dbus_g_proxy_call_no_reply(proxy, "send", G_TYPE_STRING, str, G_TYPE_INVALID);
01142 }
01143 
01144 int start_dbus();
01145 
01146 Main::Main() : kit(0) {
01147        bindtextdomain("easystroke", is_dir("po") ? "po" : LOCALEDIR);
01148        bind_textdomain_codeset("easystroke", "UTF-8");
01149        textdomain("easystroke");
01150        if (0) {
01151               RStroke trefoil = Stroke::trefoil();
01152               trefoil->draw_svg("easystroke.svg");
01153               exit(EXIT_SUCCESS);
01154        }
01155        if (argc > 1 && !strcmp(argv[1], "send")) {
01156               if (argc == 2)
01157                      usage(argv[0], false);
01158               gtk_init(&argc, &argv);
01159               send_dbus(argv[2]);
01160               exit(EXIT_SUCCESS);
01161        }
01162 
01163        display = parse_args_and_init_gtk();
01164        create_config_dir();
01165        unsetenv("DESKTOP_AUTOSTART_ID");
01166 
01167        signal(SIGINT, &quit);
01168        signal(SIGCHLD, SIG_IGN);
01169 
01170        Gtk::LinkButton::set_uri_hook(sigc::ptr_fun(&link_button_hook));
01171        Gtk::AboutDialog::set_url_hook(sigc::ptr_fun(&about_dialog_hook));
01172 
01173        dpy = XOpenDisplay(display.c_str());
01174        if (!dpy) {
01175               printf(_("Couldn't open display.\n"));
01176               exit(EXIT_FAILURE);
01177        }
01178        if (!no_dbus && start_dbus() < 0) {
01179               printf(_("Easystroke is already running, showing configuration window instead.\n"));
01180               send_dbus("");
01181               exit(EXIT_SUCCESS);
01182        }
01183        ROOT = DefaultRootWindow(dpy);
01184 
01185        ping_window = XCreateSimpleWindow(dpy, ROOT, 0, 0, 1, 1, 0, 0, 0);
01186 
01187        prefs.init();
01188        action_watcher = new ActionDBWatcher;
01189        action_watcher->init();
01190 
01191        grabber = new Grabber;
01192        // Force enter events to be generated
01193        XGrabPointer(dpy, ROOT, False, 0, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
01194        XUngrabPointer(dpy, CurrentTime);
01195 
01196        trace.reset(init_trace());
01197        Glib::RefPtr<Gdk::Screen> screen = Gdk::Display::get_default()->get_default_screen();
01198        g_signal_connect(screen->gobj(), "composited-changed", &schedule_reload_trace, NULL);
01199        screen->signal_size_changed().connect(sigc::ptr_fun(&schedule_reload_trace));
01200        Notifier *trace_notify = new Notifier(sigc::ptr_fun(&schedule_reload_trace));
01201        prefs.trace.connect(trace_notify);
01202        prefs.color.connect(trace_notify);
01203 
01204        handler = new IdleHandler;
01205        handler->init();
01206        XTestGrabControl(dpy, True);
01207 
01208 }
01209 
01210 extern const char *gui_buffer;
01211 
01212 void Main::run() {
01213        Glib::RefPtr<Glib::IOSource> io = Glib::IOSource::create(ConnectionNumber(dpy), Glib::IO_IN);
01214        io->connect(sigc::mem_fun(*this, &Main::handle));
01215        io->attach();
01216        try {
01217               widgets = Gtk::Builder::create_from_string(gui_buffer);
01218        } catch (Gtk::BuilderError &e) {
01219               printf("Error building GUI: %s\n", e.what().c_str());
01220               exit(EXIT_FAILURE);
01221        }
01222        win = new Win;
01223        if (show_gui)
01224               win->get_window().show();
01225        Gtk::Main::run();
01226        delete win;
01227 }
01228 
01229 void Main::usage(char *me, bool good) {
01230        printf("The full easystroke documentation is available at the following address:\n");
01231        printf("\n");
01232        printf("http://easystroke.wiki.sourceforge.net/Documentation#content\n");
01233        printf("\n");
01234        printf("Usage: %s [OPTION]...\n", me);
01235        printf("or:    %s send <action_name>\n", me);
01236        printf("\n");
01237        printf("Options:\n");
01238        printf("  -c, --config-dir       Directory for config files\n");
01239        printf("      --display          X Server to contact\n");
01240        printf("  -D  --no-dbus          Don't try to register as a DBus service\n");
01241        printf("  -e  --experimental     Start in experimental mode\n");
01242        printf("  -g, --show-gui         Show the configuration dialog on startup\n");
01243        printf("  -x  --disable          Start disabled\n");
01244        printf("  -v, --verbose          Increase verbosity level\n");
01245        printf("  -h, --help             Display this help and exit\n");
01246        printf("      --version          Output version information and exit\n");
01247        exit(good ? EXIT_SUCCESS : EXIT_FAILURE);
01248 }
01249 
01250 extern const char *version_string;
01251 void Main::version() {
01252        printf("easystroke %s\n", version_string);
01253        printf("\n");
01254        printf("Written by Thomas Jaeger <ThJaeger@gmail.com>.\n");
01255        exit(EXIT_SUCCESS);
01256 }
01257 
01258 std::string Main::parse_args_and_init_gtk() {
01259        static struct option long_opts1[] = {
01260               {"display",1,0,'d'},
01261               {"help",0,0,'h'},
01262               {"version",0,0,'V'},
01263               {"show-gui",0,0,'g'},
01264               {0,0,0,0}
01265        };
01266        static struct option long_opts2[] = {
01267               {"config-dir",1,0,'c'},
01268               {"display",1,0,'d'},
01269               {"experimental",0,0,'e'},
01270               {"show-gui",0,0,'g'},
01271               {"verbose",0,0,'v'},
01272               {"no-dbus",0,0,'D'},
01273               {"disabled",0,0,'x'},
01274               {0,0,0,0}
01275        };
01276        std::string display;
01277        int opt;
01278        // parse --display here, before Gtk::Main(...) takes it away from us
01279        opterr = 0;
01280        while ((opt = getopt_long(argc, argv, "gh", long_opts1, 0)) != -1)
01281               switch (opt) {
01282                      case 'd':
01283                             display = optarg;
01284                             break;
01285                      case 'g':
01286                             show_gui = true;
01287                             break;
01288                      case 'h':
01289                             usage(argv[0], true);
01290                             break;
01291                      case 'V':
01292                             version();
01293                             break;
01294               }
01295        optind = 1;
01296        opterr = 1;
01297        kit = new Gtk::Main(argc, argv);
01298        oldHandler = XSetErrorHandler(xErrorHandler);
01299        oldIOHandler = XSetIOErrorHandler(xIOErrorHandler);
01300 
01301        while ((opt = getopt_long(argc, argv, "c:egvDx", long_opts2, 0)) != -1) {
01302               switch (opt) {
01303                      case 'c':
01304                             config_dir = optarg;
01305                             break;
01306                      case 'e':
01307                             experimental = true;
01308                             break;
01309                      case 'v':
01310                             verbosity++;
01311                             break;
01312                      case 'D':
01313                             no_dbus = true;
01314                             break;
01315                      case 'x':
01316                             disabled.set(true);
01317                             break;
01318                      case 'd':
01319                      case 'n':
01320                      case 'g':
01321                             break;
01322                      default:
01323                             usage(argv[0], false);
01324               }
01325        }
01326        return display;
01327 }
01328 
01329 void Main::create_config_dir() {
01330        if (config_dir == "") {
01331               config_dir = getenv("HOME");
01332               config_dir += "/.easystroke";
01333        }
01334        struct stat st;
01335        char *name = canonicalize_file_name(config_dir.c_str());
01336 
01337        // check if the directory does not exist
01338        if (lstat(name, &st) == -1) {
01339               if (mkdir(config_dir.c_str(), 0777) == -1) {
01340                      printf(_("Error: Couldn't create configuration directory \"%s\"\n"), config_dir.c_str());
01341                      exit(EXIT_FAILURE);
01342               }
01343        } else {
01344               if (!S_ISDIR(st.st_mode)) {
01345                      printf(_("Error: \"%s\" is not a directory\n"), config_dir.c_str());
01346                      exit(EXIT_FAILURE);
01347               }
01348 
01349 
01350        }
01351        free (name);
01352        config_dir += "/";
01353 }
01354 
01355 extern Window get_app_window(Window w);
01356 
01357 void Main::handle_enter_leave(XEvent &ev) {
01358        if (ev.xcrossing.mode == NotifyGrab)
01359               return;
01360        if (ev.xcrossing.detail == NotifyInferior)
01361               return;
01362        Window w = ev.xcrossing.window;
01363        if (ev.type == EnterNotify) {
01364               current_app_window.set(get_app_window(w));
01365               if (verbosity >= 3)
01366                      printf("Entered window 0x%lx -> 0x%lx\n", w, current_app_window.get());
01367        } else printf("Error: Bogus Enter/Leave event\n");
01368 }
01369 
01370 #define H (handler->top())
01371 void Main::handle_event(XEvent &ev) {
01372 
01373        switch(ev.type) {
01374        case EnterNotify:
01375        case LeaveNotify:
01376               handle_enter_leave(ev);
01377               return;
01378 
01379        case PropertyNotify:
01380               if (current_app_window.get() == ev.xproperty.window && ev.xproperty.atom == XA_WM_CLASS)
01381                      current_app_window.notify();
01382               return;
01383 
01384        case ButtonPress:
01385               if (verbosity >= 3)
01386                      printf("Press (master): %d (%d, %d) at t = %ld\n", ev.xbutton.button, ev.xbutton.x, ev.xbutton.y, ev.xbutton.time);
01387                      H->press_master(ev.xbutton.button, ev.xbutton.time);
01388               return;
01389 
01390        case ClientMessage:
01391               if (ev.xclient.window != ping_window)
01392                      return;
01393               if (ev.xclient.message_type == *EASYSTROKE_PING) {
01394                      if (verbosity >= 3)
01395                             printf("Pong\n");
01396                      H->pong();
01397               }
01398               return;
01399 
01400        case MappingNotify:
01401               if (ev.xmapping.request != MappingPointer)
01402                      return;
01403               update_core_mapping();
01404               return;
01405        case GenericEvent:
01406               if (ev.xcookie.extension == grabber->opcode && XGetEventData(dpy, &ev.xcookie)) {
01407                      handle_xi2_event((XIDeviceEvent *)ev.xcookie.data);
01408                      XFreeEventData(dpy, &ev.xcookie);
01409               }
01410        }
01411 }
01412 
01413 static void print_coordinates(XIValuatorState *valuators, double *values) {
01414        int n = 0;
01415        for (int i = valuators->mask_len - 1; i >= 0; i--)
01416               if (XIMaskIsSet(valuators->mask, i)) {
01417                      n = i+1;
01418                      break;
01419               }
01420        bool first = true;
01421        int elt = 0;
01422        for (int i = 0; i < n; i++) {
01423               if (first)
01424                      first = false;
01425               else
01426                      printf(", ");
01427               if (XIMaskIsSet(valuators->mask, i))
01428                      printf("%.3f", values[elt++]);
01429               else
01430                      printf("*");
01431        }
01432 }
01433 
01434 void Main::report_xi2_event(XIDeviceEvent *event, const char *type) {
01435        printf("%s (XI2): ", type);
01436        if (event->detail)
01437               printf("%d ", event->detail);
01438        printf("(%.3f, %.3f) - (", event->root_x, event->root_y);
01439        print_coordinates(&event->valuators, event->valuators.values);
01440        printf(") at t = %ld\n", event->time);
01441 }
01442 
01443 void Main::handle_xi2_event(XIDeviceEvent *event) {
01444        switch (event->evtype) {
01445               case XI_ButtonPress:
01446                      if (verbosity >= 3)
01447                             report_xi2_event(event, "Press");
01448                      if (xinput_pressed.size()) {
01449                             if (!current_dev || current_dev->dev != event->deviceid)
01450                                    break;
01451                      }
01452                      current_dev = grabber->get_xi_dev(event->deviceid);
01453                      if (!current_dev) {
01454                             printf("Warning: Spurious device event\n");
01455                             break;
01456                      }
01457                      if (current_dev->master)
01458                             XISetClientPointer(dpy, None, current_dev->master);
01459                      xinput_pressed.insert(event->detail);
01460                      H->press(event->detail, create_triple(event->root_x, event->root_y, event->time));
01461                      break;
01462               case XI_ButtonRelease:
01463                      if (verbosity >= 3)
01464                             report_xi2_event(event, "Release");
01465                      if (!current_dev || current_dev->dev != event->deviceid)
01466                             break;
01467                      xinput_pressed.erase(event->detail);
01468                      H->release(event->detail, create_triple(event->root_x, event->root_y, event->time));
01469                      break;
01470               case XI_Motion:
01471                      if (verbosity >= 5)
01472                             report_xi2_event(event, "Motion");
01473                      if (!current_dev || current_dev->dev != event->deviceid)
01474                             break;
01475                      if (current_dev->supports_pressure && XIMaskIsSet(event->valuators.mask, 2)) {
01476                             int i = 0;
01477                             if (XIMaskIsSet(event->valuators.mask, 0))
01478                                    i++;
01479                             if (XIMaskIsSet(event->valuators.mask, 1))
01480                                    i++;
01481                             int z = current_dev->normalize_pressure(event->valuators.values[i]);
01482                             if (prefs.pressure_abort.get() && z >= prefs.pressure_threshold.get())
01483                                    H->pressure();
01484                      }
01485                      H->motion(create_triple(event->root_x, event->root_y, event->time));
01486                      break;
01487               case XI_RawMotion:
01488                      handle_raw_motion((XIRawEvent *)event);
01489                      break;
01490               case XI_HierarchyChanged:
01491                      grabber->hierarchy_changed((XIHierarchyEvent *)event);
01492        }
01493 }
01494 
01495 void Main::handle_raw_motion(XIRawEvent *event) {
01496        if (!current_dev || current_dev->dev != event->deviceid)
01497               return;
01498        double x = 0.0, y = 0.0;
01499        bool abs_x = current_dev->absolute;
01500        bool abs_y = current_dev->absolute;
01501        int i = 0;
01502 
01503        if (XIMaskIsSet(event->valuators.mask, 0))
01504               x = event->raw_values[i++];
01505        else
01506               abs_x = false;
01507 
01508        if (XIMaskIsSet(event->valuators.mask, 1))
01509               y = event->raw_values[i++];
01510        else
01511               abs_y = false;
01512 
01513        if (verbosity >= 5) {
01514               printf("Raw motion (XI2): (");
01515               print_coordinates(&event->valuators, event->raw_values);
01516               printf(") at t = %ld\n", event->time);
01517        }
01518 
01519        H->raw_motion(create_triple(x * current_dev->scale_x, y * current_dev->scale_y, event->time), abs_x, abs_y);
01520 }
01521 
01522 #undef H
01523 
01524 bool Main::handle(Glib::IOCondition) {
01525        while (XPending(dpy)) {
01526               try {
01527                      XEvent ev;
01528                      XNextEvent(dpy, &ev);
01529                      if (!grabber->handle(ev))
01530                             handle_event(ev);
01531               } catch (GrabFailedException &e) {
01532                      printf(_("Error: %s\n"), e.what());
01533                      bail_out();
01534               }
01535        }
01536        return true;
01537 }
01538 
01539 Main::~Main() {
01540        trace->end();
01541        trace.reset();
01542        delete grabber;
01543        delete kit;
01544        XCloseDisplay(dpy);
01545        prefs.execute_now();
01546        action_watcher->execute_now();
01547 }
01548 
01549 int main(int argc_, char **argv_) {
01550        argc = argc_;
01551        argv = argv_;
01552        Main mn;
01553        mn.run();
01554        if (verbosity >= 2)
01555               printf("Exiting...\n");
01556        return EXIT_SUCCESS;
01557 }
01558 
01559 std::string select_window() {
01560        queue(sigc::ptr_fun(&SelectHandler::create));
01561        gtk_main();
01562        win->get_window().raise();
01563        return grabber->current_class->get();
01564 }
01565 
01566 void Button::run() {
01567        grabber->suspend();
01568        fake_click(button);
01569        grabber->resume();
01570 }
01571 
01572 void SendKey::run() {
01573        if (!key)
01574               return;
01575        guint code = XKeysymToKeycode(dpy, key);
01576        XTestFakeKeyEvent(dpy, code, true, 0);
01577        XTestFakeKeyEvent(dpy, code, false, 0);
01578 }
01579 
01580 void fake_unicode(gunichar c) {
01581        static const KeySym numcode[10] = { XK_0, XK_1, XK_2, XK_3, XK_4, XK_5, XK_6, XK_7, XK_8, XK_9 };
01582        static const KeySym hexcode[6] = { XK_a, XK_b, XK_c, XK_d, XK_e, XK_f };
01583 
01584        if (verbosity >= 3) {
01585               char buf[7];
01586               buf[g_unichar_to_utf8(c, buf)] = '\0';
01587               printf("using unicode input for character %s\n", buf);
01588        }
01589        XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, XK_Control_L), true, 0);
01590        XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, XK_Shift_L), true, 0);
01591        XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, XK_u), true, 0);
01592        XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, XK_u), false, 0);
01593        XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, XK_Shift_L), false, 0);
01594        XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, XK_Control_L), false, 0);
01595        char buf[16];
01596        snprintf(buf, sizeof(buf), "%x", c);
01597        for (int i = 0; buf[i]; i++)
01598               if (buf[i] >= '0' && buf[i] <= '9') {
01599                      XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, numcode[buf[i]-'0']), true, 0);
01600                      XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, numcode[buf[i]-'0']), false, 0);
01601               } else if (buf[i] >= 'a' && buf[i] <= 'f') {
01602                      XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, hexcode[buf[i]-'a']), true, 0);
01603                      XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, hexcode[buf[i]-'a']), false, 0);
01604               }
01605        XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, XK_space), true, 0);
01606        XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, XK_space), false, 0);
01607 }
01608 
01609 bool fake_char(gunichar c) {
01610        char buf[16];
01611        snprintf(buf, sizeof(buf), "U%04X", c);
01612        KeySym keysym = XStringToKeysym(buf);
01613        if (keysym == NoSymbol)
01614               return false;
01615        KeyCode keycode = XKeysymToKeycode(dpy, keysym);
01616        if (!keycode)
01617               return false;
01618        KeyCode modifier = 0;
01619        if (XKeycodeToKeysym(dpy, keycode, 0) != keysym) {
01620               int i;
01621               for (i = 1; i < 8; i++)
01622                      if (XKeycodeToKeysym(dpy, keycode, i) == keysym)
01623                             break;
01624               if (i == 8)
01625                      return false;
01626               XModifierKeymap *keymap = XGetModifierMapping(dpy);
01627               modifier = keymap->modifiermap[i];
01628               XFreeModifiermap(keymap);
01629        }
01630        if (modifier)
01631               XTestFakeKeyEvent(dpy, modifier, true, 0);
01632        XTestFakeKeyEvent(dpy, keycode, true, 0);
01633        XTestFakeKeyEvent(dpy, keycode, false, 0);
01634        if (modifier)
01635               XTestFakeKeyEvent(dpy, modifier, false, 0);
01636        return true;
01637 }
01638 
01639 void SendText::run() {
01640        for (Glib::ustring::iterator i = text.begin(); i != text.end(); i++)
01641               if (!fake_char(*i))
01642                      fake_unicode(*i);
01643 }
01644 
01645 static struct {
01646        guint mask;
01647        guint sym;
01648 } modkeys[] = {
01649        {GDK_SHIFT_MASK, XK_Shift_L},
01650        {GDK_CONTROL_MASK, XK_Control_L},
01651        {GDK_MOD1_MASK, XK_Alt_L},
01652        {GDK_MOD2_MASK, 0},
01653        {GDK_MOD3_MASK, 0},
01654        {GDK_MOD4_MASK, 0},
01655        {GDK_MOD5_MASK, 0},
01656        {GDK_SUPER_MASK, XK_Super_L},
01657        {GDK_HYPER_MASK, XK_Hyper_L},
01658        {GDK_META_MASK, XK_Meta_L},
01659 };
01660 static int n_modkeys = 10;
01661 
01662 class Modifiers : Timeout {
01663        static std::set<Modifiers *> all;
01664        static void update_mods() {
01665               static guint mod_state = 0;
01666               guint new_state = 0;
01667               for (std::set<Modifiers *>::iterator i = all.begin(); i != all.end(); i++)
01668                      new_state |= (*i)->mods;
01669               for (int i = 0; i < n_modkeys; i++) {
01670                      guint mask = modkeys[i].mask;
01671                      if ((mod_state & mask) ^ (new_state & mask))
01672                             XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, modkeys[i].sym), new_state & mask, 0);
01673               }
01674               mod_state = new_state;
01675        }
01676 
01677        guint mods;
01678        Glib::ustring str;
01679        OSD *osd;
01680 public:
01681        Modifiers(guint mods_, Glib::ustring str_) : mods(mods_), str(str_), osd(NULL) {
01682               if (prefs.show_osd.get())
01683                      set_timeout(150);
01684               all.insert(this);
01685               update_mods();
01686        }
01687        bool operator==(const Modifiers &m) {
01688               return mods == m.mods && str == m.str;
01689        }
01690        virtual void timeout() {
01691               osd = new OSD(str);
01692        }
01693        ~Modifiers() {
01694               all.erase(this);
01695               update_mods();
01696               delete osd;
01697        }
01698 };
01699 std::set<Modifiers *> Modifiers::all;
01700 
01701 RModifiers ModAction::prepare() {
01702        return RModifiers(new Modifiers(mods, get_label()));
01703 }
01704 
01705 RModifiers SendKey::prepare() {
01706        if (!mods)
01707               return RModifiers();
01708        return RModifiers(new Modifiers(mods, ModAction::get_label()));
01709 }
01710 
01711 static bool mods_equal(RModifiers m1, RModifiers m2) {
01712        return m1 && m2 && *m1 == *m2;
01713 }
01714 
01715 void Misc::run() {
01716        switch (type) {
01717               case SHOWHIDE:
01718                      win->show_hide();
01719                      return;
01720               case UNMINIMIZE:
01721                      grabber->unminimize();
01722                      return;
01723               case DISABLE:
01724                      disabled.set(!disabled.get());
01725                      return;
01726               default:
01727                      return;
01728        }
01729 }
01730 
01731 bool is_file(std::string filename) {
01732        struct stat st;
01733        return lstat(filename.c_str(), &st) != -1 && S_ISREG(st.st_mode);
01734 }
01735 
01736 bool is_dir(std::string dirname) {
01737        struct stat st;
01738        return lstat(dirname.c_str(), &st) != -1 && S_ISDIR(st.st_mode);
01739 }