// 1,selectのループを抽象化している、今のところはgdkの機能を使って、 // GUI のループと統合をしている。 // 2,event dispatch for GtkWidget and Xlib Window. // 3,XIMの通信の確立を行う。 #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef USE_GNOME_APPLET #include #endif #include #include #include #include #include #include #include "jmode.h" #include "gtkdispatch.h" #include "xdispatch.h" #include "xim.h" static Atom xim_servers; static Atom server_heke; static Atom locales; static Atom transport; // selectionを受けるためのウィンドウ Window gWnd; //汎用のディスプレイ接続 Display *gDpy; int dpy_error_count; struct widget_watch_struct { GtkWidget *w; WidgetIf *i; int mask; }; static Dispatcher *dispatcher; static int (*default_error_handler)(Display *d, XErrorEvent *e); static int X_ErrorHandler(Display *d, XErrorEvent *e) { if (d != gDpy) { return default_error_handler(d, e); } dpy_error_count ++; printf("X error occured.\n"); return 0; } static int (*default_io_error_handler)(Display *d); static int X_IOErrorHandler(Display *d) { if (d != gDpy) { return default_io_error_handler(d); } printf("X IO error occured.\n"); global_finalize(0); return 0; } class GtkFDDispatcher : public Dispatcher { public: virtual ~GtkFDDispatcher(){}; virtual void addFDWatch(int fd, int mask, void (*f)(int, int)); virtual void removeFDWatch(int fd); virtual void mainLoop(); void fdEventHandler(int fd, GdkInputCondition c); private: struct fd_watch_struct { int read_tag,write_tag; int mask; void (*fn)(int,int); }; static void fdEventHandlerGate(gpointer p, gint fd, GdkInputCondition c); void removeCurrentWatch(int fd); std::map fd_watch_stat; }; static std::map window_watch_stat; static std::mapwidget_watch_stat; // fd watchの部分 void GtkFDDispatcher::fdEventHandler(int fd, GdkInputCondition c) { // GdkInputConditionはenumなのに or されてくる。 std::map::iterator i; int ev; i = fd_watch_stat.find(fd); if (i == fd_watch_stat.end()) { // danger .. printf("Unknown fd %d event.\n", fd); return ; } ev = 0; if (c & GDK_INPUT_READ) { (*i).second.fn(fd, READ_OK); } if (c & GDK_INPUT_WRITE) { (*i).second.fn(fd, WRITE_OK); } } void GtkFDDispatcher::fdEventHandlerGate(gpointer p, gint fd, GdkInputCondition c) { GtkFDDispatcher *dpt = (GtkFDDispatcher *)p; dpt->fdEventHandler(fd, c); } void GtkFDDispatcher::removeCurrentWatch(int fd) { std::map::iterator i; i = fd_watch_stat.find(fd); if (i == fd_watch_stat.end()) { return ; } if ((*i).second.mask & READ_OK) { gdk_input_remove((*i).second.read_tag); } if ((*i).second.mask & WRITE_OK) { gdk_input_remove((*i).second.write_tag); } } Dispatcher *get_dispatcher() { if (!dispatcher) { dispatcher = new GtkFDDispatcher(); } return dispatcher; } void GtkFDDispatcher::addFDWatch(int fd, int mask, void (*fn)(int , int)) { removeCurrentWatch(fd); int rtag=0, wtag=0; if (mask & READ_OK) { rtag = gdk_input_add(fd, (GdkInputCondition)GDK_INPUT_READ, fdEventHandlerGate, this); } if (mask & WRITE_OK) { wtag = gdk_input_add(fd, (GdkInputCondition)GDK_INPUT_WRITE, fdEventHandlerGate, this); } fd_watch_struct s; s.mask = mask; s.fn = fn; s.read_tag = rtag; s.write_tag = wtag; std::pair p(fd, s); fd_watch_stat.insert(p); } void GtkFDDispatcher::mainLoop() { #ifdef USE_GNOME_APPLET if (!(g_option_mask & OPT_TOOLBAR)){ applet_widget_gtk_main(); } else #endif gtk_main(); } void GtkFDDispatcher::removeFDWatch(int fd) { std::map::iterator i; i = fd_watch_stat.find(fd); if (i != fd_watch_stat.end()) { if ((*i).second.mask & READ_OK) { gdk_input_remove((*i).second.read_tag); } if ((*i).second.mask & WRITE_OK) { gdk_input_remove((*i).second.write_tag); } fd_watch_stat.erase(i); } } void add_window_watch(Window id, WindowIf *w, int mask) { std::pair p(id, w); window_watch_stat.insert(p); //イベントマスクはX.hに定義されているものと同じ値を持つが、 //それに依存したプログラムは書かない。 int emask = 0; if (mask & EXPOSE_MASK) { emask |= ExposureMask; } if (mask & STRUCTURE_NOTIFY_MASK) { emask |= StructureNotifyMask; } XSelectInput(gDpy, id, emask); } void remove_window_watch(Window id) { std::map::iterator i; i = window_watch_stat.find(id); if (i != window_watch_stat.end()) { window_watch_stat.erase(i); } } // GtkWidgetのイベントハンドラ // static widget_watch_struct * find_widget_watch(GtkWidget *g) { std::map::iterator i; i = widget_watch_stat.find(g); if (i != widget_watch_stat.end()) { return (*i).second; } return NULL; } static void widget_destroy(GtkWidget *w, gpointer p) { std::map::iterator i; i = widget_watch_stat.find(w); if (i != widget_watch_stat.end()) { widget_watch_struct *s; s = (*i).second; if (s->mask & WIDGET_DESTROY) { s->i->destroy(w); } free(s); widget_watch_stat.erase(i); } } static gint expose_event(GtkWidget *g, GdkEventExpose *e) { widget_watch_struct *w; w = find_widget_watch(g); if (w) { w->i->expose(g); } return FALSE; } static void clicked_event(GtkWidget *g, gpointer p) { widget_watch_struct *w; w = find_widget_watch(g); if (w) { w->i->clicked(g); } } static gint activate_event(GtkWidget *g, gpointer p) { widget_watch_struct *w; w = find_widget_watch(g); if (w) { w->i->activate(g, p); } return TRUE; } static void select_row_event(GtkWidget *g, gint row, gint column, GdkEventButton *, gpointer *) { widget_watch_struct *w; w = find_widget_watch(g); if (w) { w->i->select_row(g, row, column); } } static void key_press_event(GtkWidget *g, GdkEventKey *e, gpointer u) { widget_watch_struct *w; w = find_widget_watch(g); if (w) { w->i->key_press(g, e); } } static void resize_event(GtkWidget *g, GdkEventConfigure *event) { widget_watch_struct *w; w = find_widget_watch(g); if (w) { w->i->resize(g, g->allocation.width, g->allocation.height); } } static void button_press_event(GtkWidget *g, GdkEventButton *event) { widget_watch_struct *w; w = find_widget_watch(g); if (w) { w->i->button_press(g, (int)event->x, (int)event->y, event->button); } } static void button_release_event(GtkWidget *g, GdkEventButton *event) { widget_watch_struct *w; w = find_widget_watch(g); if (w) { w->i->button_release(g, (int)event->x, (int)event->y, event->button); } } static void motion_notify_event(GtkWidget *g, GdkEventMotion *event) { widget_watch_struct *w; w = find_widget_watch(g); if (w) { w->i->motion(g, (int)event->x, (int)event->y, event->state); } } static gint delete_event(GtkWidget *g, GdkEvent *e, gpointer p) { return FALSE; } static char * signal_name(int m) { switch (m) { case WIDGET_DESTROY:return "destroy"; case WIDGET_EXPOSE:return "expose_event"; case WIDGET_BUTTON_PRESS:return "button_press_event"; case WIDGET_CLICK:return "clicked"; case WIDGET_ACTIVATE:return "activate"; case WIDGET_DELETE:return "delete_event"; case WIDGET_ROW_SELECTED:return "select_row"; case WIDGET_KEY_PRESS:return "key_press_event"; case WIDGET_RESIZE:return "configure_event"; case WIDGET_MOTION:return "motion_notify_event"; case WIDGET_BUTTON_RELEASE:return "button_release_event"; } printf("unknown signal\n"); return 0; } void add_widget_watch(GtkWidget *g, int mask, WidgetIf *w, void *opt) { widget_watch_struct *s =find_widget_watch(g); if (!s) { //初登録 s = (widget_watch_struct *) malloc(sizeof(widget_watch_struct)); s->w = g; s->i = w; s->mask = 0; gtk_signal_connect(GTK_OBJECT(g), signal_name(WIDGET_DESTROY), GTK_SIGNAL_FUNC(widget_destroy), opt); } int emask = 0; s->mask |= mask; if (mask & WIDGET_EXPOSE) { gtk_signal_connect(GTK_OBJECT(g), signal_name(WIDGET_EXPOSE), GTK_SIGNAL_FUNC(expose_event), opt); emask |= GDK_EXPOSURE_MASK; } if (mask & WIDGET_CLICK) { gtk_signal_connect(GTK_OBJECT(g), signal_name(WIDGET_CLICK), GTK_SIGNAL_FUNC(clicked_event), opt); } if (mask & WIDGET_ACTIVATE) { gtk_signal_connect(GTK_OBJECT(g), signal_name(WIDGET_ACTIVATE), GTK_SIGNAL_FUNC(activate_event), opt); } if (mask & WIDGET_DELETE) { gtk_signal_connect(GTK_OBJECT(g), signal_name(WIDGET_DELETE), GTK_SIGNAL_FUNC(delete_event), opt); } if (mask & WIDGET_ROW_SELECTED) { gtk_signal_connect(GTK_OBJECT(g), signal_name(WIDGET_ROW_SELECTED), GTK_SIGNAL_FUNC(select_row_event), opt); } if (mask & WIDGET_KEY_PRESS) { gtk_signal_connect(GTK_OBJECT(g), signal_name(WIDGET_KEY_PRESS), GTK_SIGNAL_FUNC(key_press_event), opt); } if (mask & WIDGET_RESIZE) { gtk_signal_connect(GTK_OBJECT(g), signal_name(WIDGET_RESIZE), GTK_SIGNAL_FUNC(resize_event), opt); } if (mask & WIDGET_BUTTON_PRESS) { gtk_signal_connect(GTK_OBJECT(g),signal_name(WIDGET_BUTTON_PRESS), GTK_SIGNAL_FUNC(button_press_event),opt); } if (mask & WIDGET_BUTTON_RELEASE) { gtk_signal_connect(GTK_OBJECT(g), signal_name(WIDGET_BUTTON_RELEASE), GTK_SIGNAL_FUNC(button_release_event), opt); } if (mask & WIDGET_MOTION) { gtk_signal_connect(GTK_OBJECT(g), signal_name(WIDGET_MOTION), GTK_SIGNAL_FUNC(motion_notify_event), opt); } if (emask) { gtk_widget_set_events(g,gtk_widget_get_events(g) | emask); } std::pair p(g, s); widget_watch_stat.insert(p); } void remove_widget_watch(GtkWidget *w) { std::map::iterator i; i = widget_watch_stat.find(w); if (i != widget_watch_stat.end()) { // XXX signal_disconnectを呼んでない。 free((*i).second); widget_watch_stat.erase(i); }else{ //見付からなかったら、先にdestroyされたとして、 //それでOK } } WidgetIf::~WidgetIf() { } // static void registerAtoms() { xim_servers = XInternAtom(gDpy, "XIM_SERVERS", 0); server_heke = XInternAtom(gDpy, "@server=jmode", 0); locales = XInternAtom(gDpy, "LOCALES", 0); transport = XInternAtom(gDpy, "TRANSPORT", 0); } static bool registerProp() { Atom type; int format; unsigned long nr_prop,nr_bytes; Atom *prop; int mode = PropModePrepend; int valuechange = 1; XGetWindowProperty(gDpy, DefaultRootWindow(gDpy), xim_servers, 0, 8192 ,False, XA_ATOM, &type, &format, &nr_prop, &nr_bytes, (unsigned char **)&prop); int i; if (type != XA_ATOM || format != 32) { mode = PropModeReplace; } else { for (i = 0 ; i < (int)nr_prop ; i++) { if (prop[i] == server_heke) { mode = PropModeAppend; valuechange = 0; break; } } } if (nr_prop) { XFree(prop); } XChangeProperty(gDpy, DefaultRootWindow(gDpy), xim_servers, XA_ATOM, 32, mode, (unsigned char *)&server_heke, valuechange ? 1 : 0); return true; } bool pretrans_register() { gDpy = XOpenDisplay(NULL); if (!gDpy) { printf("failed to open display!\n"); return false; } registerAtoms(); if (!registerProp()) { XCloseDisplay(gDpy); return false; } XFlush(gDpy); scr_width = DisplayWidth(gDpy, 0); scr_height = DisplayHeight(gDpy, 0); return true; } static void sendSelectionNotify(XEvent *ev, char *buf, int len) { XEvent e; e.type = SelectionNotify; e.xselection.requestor = ev->xselectionrequest.requestor; e.xselection.selection = ev->xselectionrequest.selection; e.xselection.target = ev->xselectionrequest.target; e.xselection.time = ev->xselectionrequest.time; e.xselection.property = ev->xselectionrequest.property; XChangeProperty(gDpy, e.xselection.requestor, e.xselection.property, e.xselection.target, 8,PropModeReplace, (unsigned char *)buf, len); XSendEvent(gDpy, e.xselection.requestor, 0, 0, &e); XFlush(gDpy); } void notifyLocale(XEvent *ev) { char buf[32]; strcpy(buf,"@locale=ja_JP"); sendSelectionNotify(ev, buf, strlen(buf)+1); if (g_option_mask & OPT_TRACE) { printf("selection notify request for locale.\n"); } } void notifyTransport(XEvent *ev) { sendSelectionNotify(ev, "@transport=X/", 13+1); if (g_option_mask & OPT_TRACE) { printf("selection notify request for transport.\n"); } } WindowIf * findWindowIf(Window w) { std::map::iterator i; i = window_watch_stat.find(w); if (i == window_watch_stat.end()) { return NULL; } return (*i).second; } WindowIf::~WindowIf() { } void WindowIf::resize(Window, int, int) { // do nothing } void ProcXEvent(XEvent *e) { Atom p; switch (e->type) { case SelectionRequest: { p = e->xselectionrequest.property; if (p == locales) { notifyLocale(e); } else if(p == transport) { notifyTransport(e); } else { printf("property %s?\n", XGetAtomName(gDpy,e->xselection.property)); break; } } break; case Expose: { if (e->xexpose.count == 0) { WindowIf *i = findWindowIf(e->xexpose.window); if (i) { i->expose(e->xexpose.window); } } } break; case ConfigureNotify: { WindowIf *i = findWindowIf(e->xconfigure.window); if (i) { i->resize(e->xconfigure.window, e->xconfigure.x, e->xconfigure.y); } } break; case DestroyNotify: { WindowIf *i = findWindowIf(e->xdestroywindow.window); if (i) { i->destroy(e->xdestroywindow.window); } remove_window_watch(e->xdestroywindow.window); } break; case ClientMessage: procXClientMessage(&e->xclient); break; default:; //printf("unknown type of X event. %d\n",e->type); } } static void xEventRead(int fd, int ev) { XFlush(gDpy); XEvent e; while (XPending(gDpy)) { XNextEvent(gDpy, &e); // gtkの設定したhandlerを上書きする default_error_handler = XSetErrorHandler(X_ErrorHandler); default_io_error_handler = XSetIOErrorHandler(X_IOErrorHandler); // ProcXEvent(&e); // 戻す XSetErrorHandler(default_error_handler); XSetIOErrorHandler(default_io_error_handler); } } int pretrans_setup() { int fd; gWnd = XCreateSimpleWindow(gDpy, DefaultRootWindow(gDpy), 0, 0, 1, 1, 1,0,0); XSetSelectionOwner(gDpy, server_heke, gWnd, CurrentTime); XSelectInput(gDpy, DefaultRootWindow(gDpy), 0); XSync(gDpy, False); fd = XConnectionNumber(gDpy); Dispatcher *dpt = get_dispatcher(); dpt->addFDWatch(fd, READ_OK, xEventRead); return fd; } /* * Local variables: * c-indent-level: 4 * c-basic-offset: 4 * End: */