// 1,selectのループを抽象化している、今のところはgdkの機能を使って、
// GUI のループと統合をしている。
// 2,event dispatch for GtkWidget and Xlib Window.
// 3,XIMの通信の確立を行う。
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <gtk/gtk.h>
#ifdef USE_GNOME_APPLET
#include <applet-widget.h>
#endif
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <unistd.h>
#include <stdio.h>
#include <map>
#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<int,fd_watch_struct> fd_watch_stat;
};
static std::map<unsigned int,WindowIf *> window_watch_stat;
static std::map<GtkWidget *,widget_watch_struct *>widget_watch_stat;
// fd watchの部分
void
GtkFDDispatcher::fdEventHandler(int fd, GdkInputCondition c)
{
// GdkInputConditionはenumなのに or されてくる。
std::map<int,fd_watch_struct>::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<int,fd_watch_struct>::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<int,fd_watch_struct> 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<int,fd_watch_struct>::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<unsigned int,WindowIf *> 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<unsigned int,WindowIf *>::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<GtkWidget *,widget_watch_struct *>::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<GtkWidget *,widget_watch_struct *>::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<GtkWidget *,widget_watch_struct *> p(g, s);
widget_watch_stat.insert(p);
}
void
remove_widget_watch(GtkWidget *w)
{
std::map<GtkWidget *,widget_watch_struct *>::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<unsigned int,WindowIf *>::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:
*/
syntax highlighted by Code2HTML, v. 0.9.1