📄 gtk_tut-23.html
字号:
on the screen, is then:<P><BLOCKQUOTE><CODE><PRE>/* Draw a rectangle on the screen */static voiddraw_brush (GtkWidget *widget, gdouble x, gdouble y){ GdkRectangle update_rect; update_rect.x = x - 5; update_rect.y = y - 5; update_rect.width = 10; update_rect.height = 10; gdk_draw_rectangle (pixmap, widget->style->black_gc, TRUE, update_rect.x, update_rect.y, update_rect.width, update_rect.height); gtk_widget_draw (widget, &update_rect);}</PRE></CODE></BLOCKQUOTE><P>After we draw the rectangle representing the brush onto the pixmap,we call the function:<P><BLOCKQUOTE><CODE><PRE>void gtk_widget_draw (GtkWidget *widget, GdkRectangle *area);</PRE></CODE></BLOCKQUOTE><P>which notifies X that the area given by the <CODE>area</CODE> parameterneeds to be updated. X will eventually generate an expose event(possibly combining the areas passed in several calls to<CODE>gtk_widget_draw()</CODE>) which will cause our expose event handlerto copy the relevant portions to the screen.<P>We have now covered the entire drawing program except for a fewmundane details like creating the main window. <P><H2><A NAME="ss23.4">23.4 Adding XInput support</A></H2><P>It is now possible to buy quite inexpensive input devices such as drawing tablets, which allow drawing with a much greaterease of artistic expression than does a mouse. The simplest wayto 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><UL><LI> Pressure sensitivity</LI><LI> Tilt reporting</LI><LI> Sub-pixel positioning</LI><LI> Multiple inputs (for example, a stylus with a point and eraser)</LI></UL><P>For information about the XInput extension, see the <A HREF="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html">XInput-HOWTO</A>.<P>If we examine the full definition of, for example, the GdkEventMotionstructure, we see that it has fields to support extended deviceinformation.<P><BLOCKQUOTE><CODE><PRE>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></CODE></BLOCKQUOTE><P><CODE>pressure</CODE> gives the pressure as a floating point number between0 and 1. <CODE>xtilt</CODE> and <CODE>ytilt</CODE> can take on values between -1 and 1, corresponding to the degree of tilt in each direction.<CODE>source</CODE> and <CODE>deviceid</CODE> specify the device for which theevent occurred in two different ways. <CODE>source</CODE> gives some simpleinformation about the type of device. It can take the enumerationvalues.<P><BLOCKQUOTE><CODE><PRE>GDK_SOURCE_MOUSEGDK_SOURCE_PENGDK_SOURCE_ERASERGDK_SOURCE_CURSOR</PRE></CODE></BLOCKQUOTE><P><CODE>deviceid</CODE> specifies a unique numeric ID for the device. This canbe used to find out further information about the device using the<CODE>gdk_input_list_devices()</CODE> call (see below). The special value<CODE>GDK_CORE_POINTER</CODE> is used for the core pointer device. (Usuallythe mouse.)<P><H3>Enabling extended device information</H3><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><BLOCKQUOTE><CODE><PRE>gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);</PRE></CODE></BLOCKQUOTE><P>By giving the value <CODE>GDK_EXTENSION_EVENTS_CURSOR</CODE> we say thatwe are interested in extension events, but only if we don't haveto draw our own cursor. See the section <A HREF="#sec_Further_Sophistications">Further Sophistications</A> belowfor more information about drawing the cursor. We could also give the values <CODE>GDK_EXTENSION_EVENTS_ALL</CODE> if we were willing to draw our own cursor, or <CODE>GDK_EXTENSION_EVENTS_NONE</CODE> to revertback to the default condition.<P>This is not completely the end of the story however. By default,no extension devices are enabled. We need a mechanism to allowusers to enable and configure their extension devices. GTK providesthe InputDialog widget to automate this process. The followingprocedure manages an InputDialog widget. It creates the dialog ifit isn't present, and raises it to the top otherwise.<P><BLOCKQUOTE><CODE><PRE>voidinput_dialog_destroy (GtkWidget *w, gpointer data){ *((GtkWidget **)data) = NULL;}voidcreate_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_MAPPED(inputd)) gtk_widget_show(inputd); else gdk_window_raise(inputd->window); }}</PRE></CODE></BLOCKQUOTE><P>(You might want to take note of the way we handle this dialog. Byconnecting to the "destroy" signal, we make sure that we don't keep apointer to dialog around after it is destroyed - that could lead to asegfault.)<P>The InputDialog has two buttons "Close" and "Save", which by defaulthave no actions assigned to them. In the above function we make"Close" hide the dialog, hide the "Save" button, since we don'timplement saving of XInput options in this program.<P><H3>Using extended device information</H3><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 thesefields will have reasonable default values even when extendedevents are not enabled.<P>Once change we do have to make is to call<CODE>gdk_input_window_get_pointer()</CODE> instead of<CODE>gdk_window_get_pointer</CODE>. This is necessary because<CODE>gdk_window_get_pointer</CODE> doesn't return the extended deviceinformation.<P><BLOCKQUOTE><CODE><PRE>void gdk_input_window_get_pointer( GdkWindow *window, guint32 deviceid, gdouble *x, gdouble *y, gdouble *pressure, gdouble *xtilt, gdouble *ytilt, GdkModifierType *mask);</PRE></CODE></BLOCKQUOTE><P>When calling this function, we need to specify the device ID aswell as the window. Usually, we'll get the device ID from the<CODE>deviceid</CODE> field of an event structure. Again, this functionwill return reasonable values when extension events are notenabled. (In this case, <CODE>event->deviceid</CODE> will have the value<CODE>GDK_CORE_POINTER</CODE>).<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 theextended information.<P><BLOCKQUOTE><CODE><PRE>static gintbutton_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 gintmotion_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></CODE></BLOCKQUOTE><P>We also need to do something with the new information. Our new<CODE>draw_brush()</CODE> function draws with a different color foreach <CODE>event->source</CODE> and changes the brush size dependingon the pressure.<P><BLOCKQUOTE><CODE><PRE>/* Draw a rectangle on the screen, size depending on pressure, and color on the type of device */static voiddraw_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_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_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></CODE></BLOCKQUOTE><P><H3>Finding out more about a device</H3><P>As an example of how to find out more about a device, our programwill print the name of the device that generates each buttonpress. To find out the name of a device, we call the function:<P><BLOCKQUOTE><CODE><PRE>GList *gdk_input_list_devices (void);</PRE></CODE></BLOCKQUOTE><P>which returns a GList (a linked list type from the glib library)of GdkDeviceInfo structures. The GdkDeviceInfo structure is definedas:<P><BLOCKQUOTE><CODE><PRE>struct _GdkDeviceInfo{ guint32 deviceid; gchar *name; GdkInputSource source; GdkInputMode mode; gint has_cursor; gint num_axes; GdkAxisUse *axes; gint num_keys; GdkDeviceKey *keys;};</PRE></CODE></BLOCKQUOTE><P>Most of these fields are configuration information that youcan ignore unless you are implemented XInput configurationsaving. The we are interested in here is <CODE>name</CODE> which issimply the name that X assigns to the device. The other fieldthat isn't configuration information is <CODE>has_cursor</CODE>. If<CODE>has_cursor</CODE> is false, then we we need to draw our owncursor. But since we've specified <CODE>GDK_EXTENSION_EVENTS_CURSOR</CODE>,we don't have to worry about this.<P>Our <CODE>print_button_press()</CODE> function simply iterates throughthe returned list until it finds a match, then prints outthe name of the device.<P><BLOCKQUOTE><CODE><PRE>static voidprint_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></CODE></BLOCKQUOTE><P>That completes the changes to `XInputize' our program. <P><H3><A NAME="sec_Further_Sophistications"></A> Further sophistications </H3><P>Although our program now supports XInput quite well, it lacks somefeatures we would want in a full-featured application. First, the userprobably doesn't want to have to configure their device each time theyrun the program, so we should allow them to save the deviceconfiguration. This is done by iterating through the return of<CODE>gdk_input_list_devices()</CODE> and writing out the configuration to afile.<P>To restore the state next time the program is run, GDK providesfunctions to change device configuration:<P><BLOCKQUOTE><CODE><PRE>gdk_input_set_extension_events()gdk_input_set_source()gdk_input_set_mode()gdk_input_set_axes()gdk_input_set_key()</PRE></CODE></BLOCKQUOTE><P>(The list returned from <CODE>gdk_input_list_devices()</CODE> should not bemodified directly.) An example of doing this can be found in thedrawing program gsumi. (Available from <A HREF="http://www.msc.cornell.edu/~otaylor/gsumi/">http://www.msc.cornell.edu/~otaylor/gsumi/</A>) Eventually, itwould be nice to have a standard way of doing this for allapplications. This probably belongs at a slightly higher level thanGTK, perhaps in the GNOME library.<P>Another major omission that we have mentioned above is the lack ofcursor drawing. Platforms other than XFree86 currently do not allowsimultaneously using a device as both the core pointer and directly byan application. See the <A HREF="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html">XInput-HOWTO</A> for more information about this. This means thatapplications that want to support the widest audience need to drawtheir own cursor.<P>An application that draws its own cursor needs to do two things:determine if the current device needs a cursor drawn or not, anddetermine if the current device is in proximity. (If the currentdevice is a drawing tablet, it's a nice touch to make the cursor disappear when the stylus is lifted from the tablet. When thedevice is touching the stylus, that is called "in proximity.")The first is done by searching the device list, as we didto find out the device name. The second is achieved by selecting"proximity_out" events. An example of drawing one's own cursor isfound in the 'testinput' program found in the GTK distribution.<P><HR NOSHADE><A HREF="gtk_tut-24.html">Next</A><A HREF="gtk_tut-22.html">Previous</A><A HREF="gtk_tut.html#toc23">Contents</A></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -