<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd"> <HTML ><HEAD ><TITLE >Adding XInput support</TITLE ><META NAME="GENERATOR" CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK REL="HOME" TITLE="GTK+ 2.0 Tutorial" HREF="book1.html"><LINK REL="UP" TITLE="Scribble, A Simple Example Drawing Program" HREF="c2422.html"><LINK REL="PREVIOUS" TITLE="The DrawingArea Widget, And Drawing" HREF="x2470.html"><LINK REL="NEXT" TITLE="Tips For Writing GTK Applications" HREF="c2615.html"></HEAD ><BODY CLASS="SECT1" BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#840084" ALINK="#0000FF" ><DIV CLASS="NAVHEADER" ><TABLE SUMMARY="Header navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TH COLSPAN="3" ALIGN="center" >GTK+ 2.0 Tutorial</TH ></TR ><TR ><TD WIDTH="10%" ALIGN="left" VALIGN="bottom" ><A HREF="x2470.html" ACCESSKEY="P" ><<< Previous</A ></TD ><TD WIDTH="80%" ALIGN="center" VALIGN="bottom" >Scribble, A Simple Example Drawing Program</TD ><TD WIDTH="10%" ALIGN="right" VALIGN="bottom" ><A HREF="c2615.html" ACCESSKEY="N" >Next >>></A ></TD ></TR ></TABLE ><HR ALIGN="LEFT" WIDTH="100%"></DIV ><DIV CLASS="SECT1" ><H1 CLASS="SECT1" ><A NAME="SEC-ADDINGXINPUTSUPPORT" >Adding XInput support</A ></H1 ><P >It is now possible to buy quite inexpensive input devices such as drawing tablets, which allow drawing with a much greater ease of artistic expression than does a mouse. The simplest way to use such devices is simply as a replacement for the mouse, but that misses out many of the advantages of these devices, such as:</P ><P ></P ><UL ><LI ><P > Pressure sensitivity</P ></LI ><LI ><P > Tilt reporting</P ></LI ><LI ><P > Sub-pixel positioning</P ></LI ><LI ><P > Multiple inputs (for example, a stylus with a point and eraser)</P ></LI ></UL ><P >For information about the XInput extension, see the <A HREF="http://www.gtk.org/~otaylor/xinput/howto/index.html" TARGET="_top" >XInput HOWTO</A >.</P ><P >If we examine the full definition of, for example, the GdkEventMotion structure, we see that it has fields to support extended device information.</P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >struct _GdkEventMotion { GdkEventType type; GdkWindow *window; guint32 time; gdouble x; gdouble y; gdouble pressure; gdouble xtilt; gdouble ytilt; guint state; gint16 is_hint; GdkInputSource source; guint32 deviceid; };</PRE ></TD ></TR ></TABLE ><P ><TT CLASS="LITERAL" >pressure</TT > gives the pressure as a floating point number between 0 and 1. <TT CLASS="LITERAL" >xtilt</TT > and <TT CLASS="LITERAL" >ytilt</TT > can take on values between -1 and 1, corresponding to the degree of tilt in each direction. <TT CLASS="LITERAL" >source</TT > and <TT CLASS="LITERAL" >deviceid</TT > specify the device for which the event occurred in two different ways. <TT CLASS="LITERAL" >source</TT > gives some simple information about the type of device. It can take the enumeration values:</P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >GDK_SOURCE_MOUSE GDK_SOURCE_PEN GDK_SOURCE_ERASER GDK_SOURCE_CURSOR</PRE ></TD ></TR ></TABLE ><P ><TT CLASS="LITERAL" >deviceid</TT > specifies a unique numeric ID for the device. This can be used to find out further information about the device using the <TT CLASS="LITERAL" >gdk_input_list_devices()</TT > call (see below). The special value <TT CLASS="LITERAL" >GDK_CORE_POINTER</TT > is used for the core pointer device. (Usually the mouse.)</P ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="AEN2557" >Enabling extended device information</A ></H2 ><P >To let GTK know about our interest in the extended device information, we merely have to add a single line to our program:</P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);</PRE ></TD ></TR ></TABLE ><P >By giving the value <TT CLASS="LITERAL" >GDK_EXTENSION_EVENTS_CURSOR</TT > we say that we are interested in extension events, but only if we don't have to draw our own cursor. See the section <A HREF="x2529.html#SEC-FURTHERSOPHISTICATIONS" > Further Sophistications </A > below for more information about drawing the cursor. We could also give the values <TT CLASS="LITERAL" >GDK_EXTENSION_EVENTS_ALL</TT > if we were willing to draw our own cursor, or <TT CLASS="LITERAL" >GDK_EXTENSION_EVENTS_NONE</TT > to revert back to the default condition.</P ><P >This is not completely the end of the story however. By default, no extension devices are enabled. We need a mechanism to allow users to enable and configure their extension devices. GTK provides the InputDialog widget to automate this process. The following procedure manages an InputDialog widget. It creates the dialog if it isn't present, and raises it to the top otherwise.</P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >void input_dialog_destroy (GtkWidget *w, gpointer data) { *((GtkWidget **)data) = NULL; } void create_input_dialog () { static GtkWidget *inputd = NULL; if (!inputd) { inputd = gtk_input_dialog_new(); gtk_signal_connect (GTK_OBJECT(inputd), "destroy", (GtkSignalFunc)input_dialog_destroy, &inputd); gtk_signal_connect_object (GTK_OBJECT(GTK_INPUT_DIALOG(inputd)->close_button), "clicked", (GtkSignalFunc)gtk_widget_hide, GTK_OBJECT(inputd)); gtk_widget_hide ( GTK_INPUT_DIALOG(inputd)->save_button); gtk_widget_show (inputd); } else { if (!gtk_widget_get_mapped(inputd)) gtk_widget_show(inputd); else gdk_window_raise(inputd->window); } }</PRE ></TD ></TR ></TABLE ><P >(You might want to take note of the way we handle this dialog. By connecting to the "destroy" signal, we make sure that we don't keep a pointer to dialog around after it is destroyed - that could lead to a segfault.)</P ><P >The InputDialog has two buttons "Close" and "Save", which by default have no actions assigned to them. In the above function we make "Close" hide the dialog, hide the "Save" button, since we don't implement saving of XInput options in this program.</P ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="AEN2570" >Using extended device information</A ></H2 ><P >Once we've enabled the device, we can just use the extended device information in the extra fields of the event structures. In fact, it is always safe to use this information since these fields will have reasonable default values even when extended events are not enabled.</P ><P >Once change we do have to make is to call <TT CLASS="LITERAL" >gdk_input_window_get_pointer()</TT > instead of <TT CLASS="LITERAL" >gdk_window_get_pointer</TT >. This is necessary because <TT CLASS="LITERAL" >gdk_window_get_pointer</TT > doesn't return the extended device information.</P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >void gdk_input_window_get_pointer( GdkWindow *window, guint32 deviceid, gdouble *x, gdouble *y, gdouble *pressure, gdouble *xtilt, gdouble *ytilt, GdkModifierType *mask);</PRE ></TD ></TR ></TABLE ><P >When calling this function, we need to specify the device ID as well as the window. Usually, we'll get the device ID from the <TT CLASS="LITERAL" >deviceid</TT > field of an event structure. Again, this function will return reasonable values when extension events are not enabled. (In this case, <TT CLASS="LITERAL" >event->deviceid</TT > will have the value <TT CLASS="LITERAL" >GDK_CORE_POINTER</TT >).</P ><P >So the basic structure of our button-press and motion event handlers doesn't change much - we just need to add code to deal with the extended information.</P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >static gboolean button_press_event( GtkWidget *widget, GdkEventButton *event ) { print_button_press (event->deviceid); if (event->button == 1 && pixmap != NULL) draw_brush (widget, event->source, event->x, event->y, event->pressure); return TRUE; } static gboolean motion_notify_event( GtkWidget *widget, GdkEventMotion *event ) { gdouble x, y; gdouble pressure; GdkModifierType state; if (event->is_hint) gdk_input_window_get_pointer (event->window, event->deviceid, &x, &y, &pressure, NULL, NULL, &state); else { x = event->x; y = event->y; pressure = event->pressure; state = event->state; } if (state & GDK_BUTTON1_MASK && pixmap != NULL) draw_brush (widget, event->source, x, y, pressure); return TRUE; }</PRE ></TD ></TR ></TABLE ><P >We also need to do something with the new information. Our new <TT CLASS="LITERAL" >draw_brush()</TT > function draws with a different color for each <TT CLASS="LITERAL" >event->source</TT > and changes the brush size depending on the pressure.</P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >/* Draw a rectangle on the screen, size depending on pressure, and color on the type of device */ static void draw_brush (GtkWidget *widget, GdkInputSource source, gdouble x, gdouble y, gdouble pressure) { GdkGC *gc; GdkRectangle update_rect; switch (source) { case GDK_SOURCE_MOUSE: gc = widget->style->dark_gc[gtk_widget_get_state (widget)]; break; case GDK_SOURCE_PEN: gc = widget->style->black_gc; break; case GDK_SOURCE_ERASER: gc = widget->style->white_gc; break; default: gc = widget->style->light_gc[gtk_widget_get_state (widget)]; } update_rect.x = x - 10 * pressure; update_rect.y = y - 10 * pressure; update_rect.width = 20 * pressure; update_rect.height = 20 * pressure; gdk_draw_rectangle (pixmap, gc, TRUE, update_rect.x, update_rect.y, update_rect.width, update_rect.height); gtk_widget_draw (widget, &update_rect); }</PRE ></TD ></TR ></TABLE ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="AEN2588" >Finding out more about a device</A ></H2 ><P >As an example of how to find out more about a device, our program will print the name of the device that generates each button press. To find out the name of a device, we call the function:</P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >GList *gdk_input_list_devices (void);</PRE ></TD ></TR ></TABLE ><P >which returns a GList (a linked list type from the GLib library) of GdkDeviceInfo structures. The GdkDeviceInfo structure is defined as:</P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >struct _GdkDeviceInfo { guint32 deviceid; gchar *name; GdkInputSource source; GdkInputMode mode; gint has_cursor; gint num_axes; GdkAxisUse *axes; gint num_keys; GdkDeviceKey *keys; };</PRE ></TD ></TR ></TABLE ><P >Most of these fields are configuration information that you can ignore unless you are implementing XInput configuration saving. The fieldwe are interested in here is <TT CLASS="LITERAL" >name</TT > which is simply the name that X assigns to the device. The other field that isn't configuration information is <TT CLASS="LITERAL" >has_cursor</TT >. If <TT CLASS="LITERAL" >has_cursor</TT > is false, then we we need to draw our own cursor. But since we've specified <TT CLASS="LITERAL" >GDK_EXTENSION_EVENTS_CURSOR</TT >, we don't have to worry about this.</P ><P >Our <TT CLASS="LITERAL" >print_button_press()</TT > function simply iterates through the returned list until it finds a match, then prints out the name of the device.</P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >static void print_button_press (guint32 deviceid) { GList *tmp_list; /* gdk_input_list_devices returns an internal list, so we shouldn't free it afterwards */ tmp_list = gdk_input_list_devices(); while (tmp_list) { GdkDeviceInfo *info = (GdkDeviceInfo *)tmp_list->data; if (info->deviceid == deviceid) { printf("Button press on device '%s'\n", info->name); return; } tmp_list = tmp_list->next; } }</PRE ></TD ></TR ></TABLE ><P >That completes the changes to "XInputize" our program.</P ></DIV ><DIV CLASS="SECT2" ><H2 CLASS="SECT2" ><A NAME="SEC-FURTHERSOPHISTICATIONS" >Further sophistications</A ></H2 ><P >Although our program now supports XInput quite well, it lacks some features we would want in a full-featured application. First, the user probably doesn't want to have to configure their device each time they run the program, so we should allow them to save the device configuration. This is done by iterating through the return of <TT CLASS="LITERAL" >gdk_input_list_devices()</TT > and writing out the configuration to a file.</P ><P >To restore the state next time the program is run, GDK provides functions to change device configuration:</P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%" ><TR ><TD ><PRE CLASS="PROGRAMLISTING" >gdk_input_set_extension_events() gdk_input_set_source() gdk_input_set_mode() gdk_input_set_axes() gdk_input_set_key()</PRE ></TD ></TR ></TABLE ><P >(The list returned from <TT CLASS="LITERAL" >gdk_input_list_devices()</TT > should not be modified directly.) An example of doing this can be found in the drawing program gsumi. (Available from <A HREF="http://www.msc.cornell.edu/~otaylor/gsumi/" TARGET="_top" >http://www.msc.cornell.edu/~otaylor/gsumi/</A >) Eventually, it would be nice to have a standard way of doing this for all applications. This probably belongs at a slightly higher level than GTK, perhaps in the GNOME library.</P ><P >Another major omission that we have mentioned above is the lack of cursor drawing. Platforms other than XFree86 currently do not allow simultaneously using a device as both the core pointer and directly by an application. See the <A HREF="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html" TARGET="_top" >XInput-HOWTO</A > for more information about this. This means that applications that want to support the widest audience need to draw their own cursor.</P ><P >An application that draws its own cursor needs to do two things: determine if the current device needs a cursor drawn or not, and determine if the current device is in proximity. (If the current device is a drawing tablet, it's a nice touch to make the cursor disappear when the stylus is lifted from the tablet. When the device is touching the stylus, that is called "in proximity.") The first is done by searching the device list, as we did to find out the device name. The second is achieved by selecting "proximity_out" events. An example of drawing one's own cursor is found in the "testinput" program found in the GTK distribution.</P ></DIV ></DIV ><DIV CLASS="NAVFOOTER" ><HR ALIGN="LEFT" WIDTH="100%"><TABLE SUMMARY="Footer navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" ><A HREF="x2470.html" ACCESSKEY="P" ><<< Previous</A ></TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="book1.html" ACCESSKEY="H" >Home</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" ><A HREF="c2615.html" ACCESSKEY="N" >Next >>></A ></TD ></TR ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" >The DrawingArea Widget, And Drawing</TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="c2422.html" ACCESSKEY="U" >Up</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" >Tips For Writing GTK Applications</TD ></TR ></TABLE ></DIV ></BODY ></HTML >