diff --git a/ChangeLog b/ChangeLog index 853a49c044..46e26f6b28 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,38 @@ +Fri Jun 5 22:37:40 1998 Owen Taylor + + * app/Makefile.am app/app_procs.c app/brushes.c app/commands.[ch] + app/disp_callbacks.c app/gdisplay.[ch] app/gimprc.c + app/interface.[ch] app/menus.c app/paint_core.[ch] + app/paintbrush.c app/palette.c app/scroll.c + app/tools.[ch] app/undo.c + + - Added two new dialogs - input devices; (GtkInputDialog) + and DeviceStatus - which shows the tool/color for + each device. + + - Added device_status_update() call that gets called + whenever the tool/color etc. are changed. + + - Added ~/.gimp/devicerc file to store settings + + - Code to draw cursor on canvas for non XFree86 XInput + where device can't control cursor and extended input + device simultaneously. + + - Changed input handling so that we always use the pointer + position from the device, not from gdk_input_window_get_cursor, + so that motion and cursor position sync. + + - Various changes so things work with non-integer coordinates + + - Pay attention to pressure and tilt in basic tool support. + + - New paint mode PRESSURE that changes the brush based on + the brush pressure + + - Left in a few XInput hacks that should be removed, but I no longer + remember what they are. + Fri Jun 5 18:08:32 PDT 1998 Manish Singh * configure.in: bumped to 1.1 diff --git a/app/Makefile.am b/app/Makefile.am index 545d3725dc..fb3eb2641c 100644 --- a/app/Makefile.am +++ b/app/Makefile.am @@ -79,6 +79,8 @@ gimp_SOURCES = \ datafiles.h \ desaturate.c \ desaturate.h \ + devices.c \ + devices.h \ dialog_types.h \ disp_callbacks.c \ disp_callbacks.h \ diff --git a/app/actions/help-commands.c b/app/actions/help-commands.c index ceaf9a3633..8e681932da 100644 --- a/app/actions/help-commands.c +++ b/app/actions/help-commands.c @@ -32,6 +32,7 @@ #include "convert.h" #include "curves.h" #include "desaturate.h" +#include "devices.h" #include "channel_ops.h" #include "drawable.h" #include "equalize.h" @@ -2436,6 +2437,20 @@ dialogs_tools_options_cmd_callback (GtkWidget *widget, tools_options_dialog_show (); } +void +dialogs_input_devices_cmd_callback (GtkWidget *widget, + gpointer client_data) +{ + create_input_dialog (); +} + +void +dialogs_device_status_cmd_callback (GtkWidget *widget, + gpointer client_data) +{ + create_device_status (); +} + void about_dialog_cmd_callback (GtkWidget *widget, gpointer client_data) diff --git a/app/actions/help-commands.h b/app/actions/help-commands.h index 5ec59cbb85..645262543a 100644 --- a/app/actions/help-commands.h +++ b/app/actions/help-commands.h @@ -89,6 +89,8 @@ void dialogs_gradient_editor_cmd_callback (GtkWidget *, gpointer); void dialogs_lc_cmd_callback (GtkWidget *, gpointer); void dialogs_indexed_palette_cmd_callback (GtkWidget *, gpointer); void dialogs_tools_options_cmd_callback (GtkWidget *, gpointer); +void dialogs_input_devices_cmd_callback (GtkWidget *, gpointer); +void dialogs_device_status_cmd_callback (GtkWidget *, gpointer); void about_dialog_cmd_callback (GtkWidget *, gpointer); void tips_dialog_cmd_callback (GtkWidget *, gpointer); diff --git a/app/app_procs.c b/app/app_procs.c index 061af08fd8..4e0f1a90d6 100644 --- a/app/app_procs.c +++ b/app/app_procs.c @@ -30,6 +30,7 @@ #include "brushes.h" #include "color_transfer.h" #include "curves.h" +#include "devices.h" #include "gdisplay.h" #include "colormaps.h" #include "fileops.h" @@ -511,6 +512,7 @@ app_init (void) if (no_interface == FALSE) { get_standard_colormaps (); + devices_init (); create_toolbox (); gximage_init (); render_setup (transparency_type, transparency_size); diff --git a/app/brushes.c b/app/brushes.c index 80ab423a89..8f27c033ee 100644 --- a/app/brushes.c +++ b/app/brushes.c @@ -29,6 +29,7 @@ #include "buildmenu.h" #include "colormaps.h" #include "datafiles.h" +#include "devices.h" #include "errors.h" #include "general.h" #include "gimprc.h" @@ -351,6 +352,8 @@ select_brush (GBrushP brush) /* Keep up appearances in the brush dialog */ if (brush_select_dialog) brush_select_select (brush_select_dialog, brush->index); + + device_status_update (current_device); } diff --git a/app/commands.c b/app/commands.c index ceaf9a3633..8e681932da 100644 --- a/app/commands.c +++ b/app/commands.c @@ -32,6 +32,7 @@ #include "convert.h" #include "curves.h" #include "desaturate.h" +#include "devices.h" #include "channel_ops.h" #include "drawable.h" #include "equalize.h" @@ -2436,6 +2437,20 @@ dialogs_tools_options_cmd_callback (GtkWidget *widget, tools_options_dialog_show (); } +void +dialogs_input_devices_cmd_callback (GtkWidget *widget, + gpointer client_data) +{ + create_input_dialog (); +} + +void +dialogs_device_status_cmd_callback (GtkWidget *widget, + gpointer client_data) +{ + create_device_status (); +} + void about_dialog_cmd_callback (GtkWidget *widget, gpointer client_data) diff --git a/app/commands.h b/app/commands.h index 5ec59cbb85..645262543a 100644 --- a/app/commands.h +++ b/app/commands.h @@ -89,6 +89,8 @@ void dialogs_gradient_editor_cmd_callback (GtkWidget *, gpointer); void dialogs_lc_cmd_callback (GtkWidget *, gpointer); void dialogs_indexed_palette_cmd_callback (GtkWidget *, gpointer); void dialogs_tools_options_cmd_callback (GtkWidget *, gpointer); +void dialogs_input_devices_cmd_callback (GtkWidget *, gpointer); +void dialogs_device_status_cmd_callback (GtkWidget *, gpointer); void about_dialog_cmd_callback (GtkWidget *, gpointer); void tips_dialog_cmd_callback (GtkWidget *, gpointer); diff --git a/app/core/gimpimage-undo-push.c b/app/core/gimpimage-undo-push.c index d3da62bd50..234d194430 100644 --- a/app/core/gimpimage-undo-push.c +++ b/app/core/gimpimage-undo-push.c @@ -1018,6 +1018,18 @@ undo_pop_paint (GImage *gimage, pc->lasty = pu->lasty; pu->lasty = tmp; + tmp = pc->lastpressure; + pc->lastpressure = pu->lastpressure; + pu->lastpressure = tmp; + + tmp = pc->lastxtilt; + pc->lastxtilt = pu->lastxtilt; + pu->lastxtilt = tmp; + + tmp = pc->lastytilt; + pc->lastytilt = pu->lastytilt; + pu->lastytilt = tmp; + return TRUE; } diff --git a/app/core/gimpprojection.c b/app/core/gimpprojection.c index 8a0203361e..503ab3c4bd 100644 --- a/app/core/gimpprojection.c +++ b/app/core/gimpprojection.c @@ -75,6 +75,7 @@ static GSList * gdisplay_process_area_list(GSList *, GArea *); static void gdisplay_add_update_area (GDisplay *, int, int, int, int); static void gdisplay_add_display_area (GDisplay *, int, int, int, int); static void gdisplay_paint_area (GDisplay *, int, int, int, int); +static void gdisplay_draw_cursor (GDisplay *); static void gdisplay_display_area (GDisplay *, int, int, int, int); static guint gdisplay_hash (GDisplay *); @@ -121,6 +122,10 @@ gdisplay_new (GImage *gimage, gdisp->draw_guides = TRUE; gdisp->snap_to_guides = TRUE; + gdisp->draw_cursor = FALSE; + gdisp->proximity = FALSE; + gdisp->have_cursor = FALSE; + /* add the new display to the list so that it isn't lost */ display_list = g_slist_append (display_list, (void *) gdisp); @@ -340,6 +345,10 @@ gdisplay_flush (GDisplay *gdisp) /* draw the guides */ gdisplay_draw_guides (gdisp); + /* and the cursor (if we have a software cursor */ + if (gdisp->have_cursor) + gdisplay_draw_cursor (gdisp); + /* restart (and recalculate) the selection boundaries */ selection_start (gdisp->select, TRUE); @@ -520,10 +529,10 @@ gdisplay_find_guide (GDisplay *gdisp, void gdisplay_snap_point (GDisplay *gdisp, - int x , - int y, - int *tx, - int *ty) + gdouble x , + gdouble y, + gdouble *tx, + gdouble *ty) { GList *tmp_list; Guide *guide; @@ -598,8 +607,8 @@ gdisplay_snap_rectangle (GDisplay *gdisp, int *tx1, int *ty1) { - int nx1, ny1; - int nx2, ny2; + double nx1, ny1; + double nx2, ny2; *tx1 = x1; *ty1 = y1; @@ -611,18 +620,73 @@ gdisplay_snap_rectangle (GDisplay *gdisp, gdisplay_snap_point (gdisp, x1, y1, &nx1, &ny1); gdisplay_snap_point (gdisp, x2, y2, &nx2, &ny2); - if (x1 != nx1) + if (x1 != (int)nx1) *tx1 = nx1; - else if (x2 != nx2) + else if (x2 != (int)nx2) *tx1 = x1 + (nx2 - x2); - if (y1 != ny1) + if (y1 != (int)ny1) *ty1 = ny1; - else if (y2 != ny2) + else if (y2 != (int)ny2) *ty1 = y1 + (ny2 - y2); } } +void +gdisplay_draw_cursor (GDisplay *gdisp) +{ + int x = gdisp->cursor_x; + int y = gdisp->cursor_y; + + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->white_gc, + x - 7, y-1, x + 7, y-1); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->black_gc, + x - 7, y, x + 7, y); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->white_gc, + x - 7, y+1, x + 7, y+1); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->white_gc, + x-1, y - 7, x-1, y + 7); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->black_gc, + x, y - 7, x, y + 7); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->white_gc, + x+1, y - 7, x+1, y + 7); +} + +void +gdisplay_update_cursor (GDisplay *gdisp, int x, int y) +{ + int new_cursor; + + new_cursor = gdisp->draw_cursor && gdisp->proximity; + + /* Erase old cursor, if necessary */ + + if (gdisp->have_cursor && (!new_cursor || x != gdisp->cursor_x || + y != gdisp->cursor_y)) + { + gdisplay_expose_area (gdisp, gdisp->cursor_x - 7, + gdisp->cursor_y - 7, + 15, 15); + if (!new_cursor) + { + gdisp->have_cursor = FALSE; + gdisplay_flush (gdisp); + } + } + + gdisp->have_cursor = new_cursor; + gdisp->cursor_x = x; + gdisp->cursor_y = y; + + if (new_cursor) + gdisplay_flush (gdisp); +} void gdisplay_remove_and_delete (GDisplay *gdisp) diff --git a/app/core/gimpprojection.h b/app/core/gimpprojection.h index 575099fc93..926413ca4f 100644 --- a/app/core/gimpprojection.h +++ b/app/core/gimpprojection.h @@ -89,6 +89,12 @@ struct _GDisplay GSList *display_areas; /* Display areas list */ GdkCursorType current_cursor; /* Currently installed cursor */ + + short draw_cursor; /* should we draw software cursor ? */ + int cursor_x; /* software cursor X value */ + int cursor_y; /* software cursor Y value */ + short proximity; /* is a device in proximity of gdisplay ? */ + short have_cursor; /* is cursor currently drawn ? */ }; @@ -116,8 +122,9 @@ void gdisplay_flush (GDisplay *); void gdisplay_draw_guides (GDisplay *); void gdisplay_draw_guide (GDisplay *, Guide *, int); Guide* gdisplay_find_guide (GDisplay *, int, int); -void gdisplay_snap_point (GDisplay *, int , int, int *, int *); +void gdisplay_snap_point (GDisplay *, double , double, double *, double *); void gdisplay_snap_rectangle (GDisplay *, int, int, int, int, int *, int *); +void gdisplay_update_cursor (GDisplay *, int, int); /* function declarations */ diff --git a/app/disp_callbacks.c b/app/disp_callbacks.c index c3976b11f0..3020d5a404 100644 --- a/app/disp_callbacks.c +++ b/app/disp_callbacks.c @@ -20,6 +20,7 @@ #include "appenv.h" #include "colormaps.h" #include "cursorutil.h" +#include "devices.h" #include "disp_callbacks.h" #include "gdisplay.h" #include "general.h" @@ -36,6 +37,10 @@ #define HORIZONTAL 1 #define VERTICAL 2 +/* Function declarations */ + +static void gdisplay_check_device_cursor (GDisplay *gdisp); + static void redraw (GDisplay *gdisp, int x, @@ -62,6 +67,29 @@ redraw (GDisplay *gdisp, } } +static void +gdisplay_check_device_cursor (GDisplay *gdisp) +{ + 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 == current_device) + { + gdisp->draw_cursor = !info->has_cursor; + break; + } + + tmp_list = tmp_list->next; + } +} + gint gdisplay_canvas_events (GtkWidget *canvas, GdkEvent *event) @@ -71,8 +99,7 @@ gdisplay_canvas_events (GtkWidget *canvas, GdkEventMotion *mevent; GdkEventButton *bevent; GdkEventKey *kevent; - gint tx, ty; - GdkModifierType tmask; + gdouble tx, ty; guint state = 0; gint return_val = FALSE; static gboolean scrolled = FALSE; @@ -111,9 +138,11 @@ gdisplay_canvas_events (GtkWidget *canvas, setup_scale (gdisp); } - /* get the pointer position */ - gdk_window_get_pointer (canvas->window, &tx, &ty, &tmask); - + /* Find out what device the event occurred upon */ + + if (devices_check_change (event)) + gdisplay_check_device_cursor (gdisp); + switch (event->type) { case GDK_EXPOSE: @@ -132,6 +161,16 @@ gdisplay_canvas_events (GtkWidget *canvas, } break; + case GDK_LEAVE_NOTIFY: + gdisp->proximity = FALSE; + gdisplay_update_cursor (gdisp, 0, 0); + break; + + case GDK_PROXIMITY_OUT: + gdisp->proximity = FALSE; + gdisplay_update_cursor (gdisp, 0, 0); + break; + case GDK_BUTTON_PRESS: bevent = (GdkEventButton *) event; state = bevent->state; @@ -254,13 +293,24 @@ gdisplay_canvas_events (GtkWidget *canvas, mevent = (GdkEventMotion *) event; state = mevent->state; + /* Ask for the pointer position, but ignore it except for cursor + * handling, so motion events sync with the button press/release events */ + if (mevent->is_hint) + gdk_input_window_get_pointer (canvas->window, current_device, &tx, &ty, + NULL, NULL, NULL, NULL); + else { - mevent->x = tx; - mevent->y = ty; - mevent->state = tmask; + tx = mevent->x; + ty = mevent->y; } + if (!gdisp->proximity) + { + gdisp->proximity = TRUE; + gdisplay_check_device_cursor (gdisp); + } + if (active_tool && ((active_tool->type == MOVE) || !gimage_is_empty (gdisp->gimage)) && (mevent->state & GDK_BUTTON1_MASK)) @@ -369,6 +419,8 @@ gdisplay_canvas_events (GtkWidget *canvas, gdisplay_install_tool_cursor (gdisp, GDK_TOP_LEFT_ARROW); } + gdisplay_update_cursor (gdisp, tx, ty); + return return_val; } diff --git a/app/disp_callbacks.h b/app/disp_callbacks.h index 2d7089095e..d7e4246063 100644 --- a/app/disp_callbacks.h +++ b/app/disp_callbacks.h @@ -21,7 +21,9 @@ #define CANVAS_EVENT_MASK GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | \ GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | \ GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK | \ - GDK_ENTER_NOTIFY_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK + GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | \ + GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | \ + GDK_PROXIMITY_OUT_MASK gint gdisplay_canvas_events (GtkWidget *, GdkEvent *); gint gdisplay_hruler_button_press (GtkWidget *, GdkEventButton *, gpointer); diff --git a/app/display/gimpdisplay-callbacks.c b/app/display/gimpdisplay-callbacks.c index c3976b11f0..3020d5a404 100644 --- a/app/display/gimpdisplay-callbacks.c +++ b/app/display/gimpdisplay-callbacks.c @@ -20,6 +20,7 @@ #include "appenv.h" #include "colormaps.h" #include "cursorutil.h" +#include "devices.h" #include "disp_callbacks.h" #include "gdisplay.h" #include "general.h" @@ -36,6 +37,10 @@ #define HORIZONTAL 1 #define VERTICAL 2 +/* Function declarations */ + +static void gdisplay_check_device_cursor (GDisplay *gdisp); + static void redraw (GDisplay *gdisp, int x, @@ -62,6 +67,29 @@ redraw (GDisplay *gdisp, } } +static void +gdisplay_check_device_cursor (GDisplay *gdisp) +{ + 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 == current_device) + { + gdisp->draw_cursor = !info->has_cursor; + break; + } + + tmp_list = tmp_list->next; + } +} + gint gdisplay_canvas_events (GtkWidget *canvas, GdkEvent *event) @@ -71,8 +99,7 @@ gdisplay_canvas_events (GtkWidget *canvas, GdkEventMotion *mevent; GdkEventButton *bevent; GdkEventKey *kevent; - gint tx, ty; - GdkModifierType tmask; + gdouble tx, ty; guint state = 0; gint return_val = FALSE; static gboolean scrolled = FALSE; @@ -111,9 +138,11 @@ gdisplay_canvas_events (GtkWidget *canvas, setup_scale (gdisp); } - /* get the pointer position */ - gdk_window_get_pointer (canvas->window, &tx, &ty, &tmask); - + /* Find out what device the event occurred upon */ + + if (devices_check_change (event)) + gdisplay_check_device_cursor (gdisp); + switch (event->type) { case GDK_EXPOSE: @@ -132,6 +161,16 @@ gdisplay_canvas_events (GtkWidget *canvas, } break; + case GDK_LEAVE_NOTIFY: + gdisp->proximity = FALSE; + gdisplay_update_cursor (gdisp, 0, 0); + break; + + case GDK_PROXIMITY_OUT: + gdisp->proximity = FALSE; + gdisplay_update_cursor (gdisp, 0, 0); + break; + case GDK_BUTTON_PRESS: bevent = (GdkEventButton *) event; state = bevent->state; @@ -254,13 +293,24 @@ gdisplay_canvas_events (GtkWidget *canvas, mevent = (GdkEventMotion *) event; state = mevent->state; + /* Ask for the pointer position, but ignore it except for cursor + * handling, so motion events sync with the button press/release events */ + if (mevent->is_hint) + gdk_input_window_get_pointer (canvas->window, current_device, &tx, &ty, + NULL, NULL, NULL, NULL); + else { - mevent->x = tx; - mevent->y = ty; - mevent->state = tmask; + tx = mevent->x; + ty = mevent->y; } + if (!gdisp->proximity) + { + gdisp->proximity = TRUE; + gdisplay_check_device_cursor (gdisp); + } + if (active_tool && ((active_tool->type == MOVE) || !gimage_is_empty (gdisp->gimage)) && (mevent->state & GDK_BUTTON1_MASK)) @@ -369,6 +419,8 @@ gdisplay_canvas_events (GtkWidget *canvas, gdisplay_install_tool_cursor (gdisp, GDK_TOP_LEFT_ARROW); } + gdisplay_update_cursor (gdisp, tx, ty); + return return_val; } diff --git a/app/display/gimpdisplay-callbacks.h b/app/display/gimpdisplay-callbacks.h index 2d7089095e..d7e4246063 100644 --- a/app/display/gimpdisplay-callbacks.h +++ b/app/display/gimpdisplay-callbacks.h @@ -21,7 +21,9 @@ #define CANVAS_EVENT_MASK GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | \ GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | \ GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK | \ - GDK_ENTER_NOTIFY_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK + GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | \ + GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | \ + GDK_PROXIMITY_OUT_MASK gint gdisplay_canvas_events (GtkWidget *, GdkEvent *); gint gdisplay_hruler_button_press (GtkWidget *, GdkEventButton *, gpointer); diff --git a/app/display/gimpdisplay-scroll.c b/app/display/gimpdisplay-scroll.c index 328b947bf8..2690e3aa18 100644 --- a/app/display/gimpdisplay-scroll.c +++ b/app/display/gimpdisplay-scroll.c @@ -102,7 +102,7 @@ void scroll_to_pointer_position (GDisplay *gdisp, GdkEventMotion *mevent) { - int child_x, child_y; + double child_x, child_y; int off_x, off_y; off_x = off_y = 0; @@ -119,7 +119,9 @@ scroll_to_pointer_position (GDisplay *gdisp, if (scroll_display (gdisp, off_x, off_y)) { - gdk_window_get_pointer (gdisp->canvas->window, &child_x, &child_y, NULL); + gdk_input_window_get_pointer (gdisp->canvas->window, mevent->deviceid, + &child_x, &child_y, + NULL, NULL, NULL, NULL); if (child_x == mevent->x && child_y == mevent->y) /* Put this event back on the queue -- so it keeps scrolling */ diff --git a/app/display/gimpdisplay.c b/app/display/gimpdisplay.c index 8a0203361e..503ab3c4bd 100644 --- a/app/display/gimpdisplay.c +++ b/app/display/gimpdisplay.c @@ -75,6 +75,7 @@ static GSList * gdisplay_process_area_list(GSList *, GArea *); static void gdisplay_add_update_area (GDisplay *, int, int, int, int); static void gdisplay_add_display_area (GDisplay *, int, int, int, int); static void gdisplay_paint_area (GDisplay *, int, int, int, int); +static void gdisplay_draw_cursor (GDisplay *); static void gdisplay_display_area (GDisplay *, int, int, int, int); static guint gdisplay_hash (GDisplay *); @@ -121,6 +122,10 @@ gdisplay_new (GImage *gimage, gdisp->draw_guides = TRUE; gdisp->snap_to_guides = TRUE; + gdisp->draw_cursor = FALSE; + gdisp->proximity = FALSE; + gdisp->have_cursor = FALSE; + /* add the new display to the list so that it isn't lost */ display_list = g_slist_append (display_list, (void *) gdisp); @@ -340,6 +345,10 @@ gdisplay_flush (GDisplay *gdisp) /* draw the guides */ gdisplay_draw_guides (gdisp); + /* and the cursor (if we have a software cursor */ + if (gdisp->have_cursor) + gdisplay_draw_cursor (gdisp); + /* restart (and recalculate) the selection boundaries */ selection_start (gdisp->select, TRUE); @@ -520,10 +529,10 @@ gdisplay_find_guide (GDisplay *gdisp, void gdisplay_snap_point (GDisplay *gdisp, - int x , - int y, - int *tx, - int *ty) + gdouble x , + gdouble y, + gdouble *tx, + gdouble *ty) { GList *tmp_list; Guide *guide; @@ -598,8 +607,8 @@ gdisplay_snap_rectangle (GDisplay *gdisp, int *tx1, int *ty1) { - int nx1, ny1; - int nx2, ny2; + double nx1, ny1; + double nx2, ny2; *tx1 = x1; *ty1 = y1; @@ -611,18 +620,73 @@ gdisplay_snap_rectangle (GDisplay *gdisp, gdisplay_snap_point (gdisp, x1, y1, &nx1, &ny1); gdisplay_snap_point (gdisp, x2, y2, &nx2, &ny2); - if (x1 != nx1) + if (x1 != (int)nx1) *tx1 = nx1; - else if (x2 != nx2) + else if (x2 != (int)nx2) *tx1 = x1 + (nx2 - x2); - if (y1 != ny1) + if (y1 != (int)ny1) *ty1 = ny1; - else if (y2 != ny2) + else if (y2 != (int)ny2) *ty1 = y1 + (ny2 - y2); } } +void +gdisplay_draw_cursor (GDisplay *gdisp) +{ + int x = gdisp->cursor_x; + int y = gdisp->cursor_y; + + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->white_gc, + x - 7, y-1, x + 7, y-1); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->black_gc, + x - 7, y, x + 7, y); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->white_gc, + x - 7, y+1, x + 7, y+1); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->white_gc, + x-1, y - 7, x-1, y + 7); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->black_gc, + x, y - 7, x, y + 7); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->white_gc, + x+1, y - 7, x+1, y + 7); +} + +void +gdisplay_update_cursor (GDisplay *gdisp, int x, int y) +{ + int new_cursor; + + new_cursor = gdisp->draw_cursor && gdisp->proximity; + + /* Erase old cursor, if necessary */ + + if (gdisp->have_cursor && (!new_cursor || x != gdisp->cursor_x || + y != gdisp->cursor_y)) + { + gdisplay_expose_area (gdisp, gdisp->cursor_x - 7, + gdisp->cursor_y - 7, + 15, 15); + if (!new_cursor) + { + gdisp->have_cursor = FALSE; + gdisplay_flush (gdisp); + } + } + + gdisp->have_cursor = new_cursor; + gdisp->cursor_x = x; + gdisp->cursor_y = y; + + if (new_cursor) + gdisplay_flush (gdisp); +} void gdisplay_remove_and_delete (GDisplay *gdisp) diff --git a/app/display/gimpdisplay.h b/app/display/gimpdisplay.h index 575099fc93..926413ca4f 100644 --- a/app/display/gimpdisplay.h +++ b/app/display/gimpdisplay.h @@ -89,6 +89,12 @@ struct _GDisplay GSList *display_areas; /* Display areas list */ GdkCursorType current_cursor; /* Currently installed cursor */ + + short draw_cursor; /* should we draw software cursor ? */ + int cursor_x; /* software cursor X value */ + int cursor_y; /* software cursor Y value */ + short proximity; /* is a device in proximity of gdisplay ? */ + short have_cursor; /* is cursor currently drawn ? */ }; @@ -116,8 +122,9 @@ void gdisplay_flush (GDisplay *); void gdisplay_draw_guides (GDisplay *); void gdisplay_draw_guide (GDisplay *, Guide *, int); Guide* gdisplay_find_guide (GDisplay *, int, int); -void gdisplay_snap_point (GDisplay *, int , int, int *, int *); +void gdisplay_snap_point (GDisplay *, double , double, double *, double *); void gdisplay_snap_rectangle (GDisplay *, int, int, int, int, int *, int *); +void gdisplay_update_cursor (GDisplay *, int, int); /* function declarations */ diff --git a/app/display/gimpdisplayshell-callbacks.c b/app/display/gimpdisplayshell-callbacks.c index c3976b11f0..3020d5a404 100644 --- a/app/display/gimpdisplayshell-callbacks.c +++ b/app/display/gimpdisplayshell-callbacks.c @@ -20,6 +20,7 @@ #include "appenv.h" #include "colormaps.h" #include "cursorutil.h" +#include "devices.h" #include "disp_callbacks.h" #include "gdisplay.h" #include "general.h" @@ -36,6 +37,10 @@ #define HORIZONTAL 1 #define VERTICAL 2 +/* Function declarations */ + +static void gdisplay_check_device_cursor (GDisplay *gdisp); + static void redraw (GDisplay *gdisp, int x, @@ -62,6 +67,29 @@ redraw (GDisplay *gdisp, } } +static void +gdisplay_check_device_cursor (GDisplay *gdisp) +{ + 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 == current_device) + { + gdisp->draw_cursor = !info->has_cursor; + break; + } + + tmp_list = tmp_list->next; + } +} + gint gdisplay_canvas_events (GtkWidget *canvas, GdkEvent *event) @@ -71,8 +99,7 @@ gdisplay_canvas_events (GtkWidget *canvas, GdkEventMotion *mevent; GdkEventButton *bevent; GdkEventKey *kevent; - gint tx, ty; - GdkModifierType tmask; + gdouble tx, ty; guint state = 0; gint return_val = FALSE; static gboolean scrolled = FALSE; @@ -111,9 +138,11 @@ gdisplay_canvas_events (GtkWidget *canvas, setup_scale (gdisp); } - /* get the pointer position */ - gdk_window_get_pointer (canvas->window, &tx, &ty, &tmask); - + /* Find out what device the event occurred upon */ + + if (devices_check_change (event)) + gdisplay_check_device_cursor (gdisp); + switch (event->type) { case GDK_EXPOSE: @@ -132,6 +161,16 @@ gdisplay_canvas_events (GtkWidget *canvas, } break; + case GDK_LEAVE_NOTIFY: + gdisp->proximity = FALSE; + gdisplay_update_cursor (gdisp, 0, 0); + break; + + case GDK_PROXIMITY_OUT: + gdisp->proximity = FALSE; + gdisplay_update_cursor (gdisp, 0, 0); + break; + case GDK_BUTTON_PRESS: bevent = (GdkEventButton *) event; state = bevent->state; @@ -254,13 +293,24 @@ gdisplay_canvas_events (GtkWidget *canvas, mevent = (GdkEventMotion *) event; state = mevent->state; + /* Ask for the pointer position, but ignore it except for cursor + * handling, so motion events sync with the button press/release events */ + if (mevent->is_hint) + gdk_input_window_get_pointer (canvas->window, current_device, &tx, &ty, + NULL, NULL, NULL, NULL); + else { - mevent->x = tx; - mevent->y = ty; - mevent->state = tmask; + tx = mevent->x; + ty = mevent->y; } + if (!gdisp->proximity) + { + gdisp->proximity = TRUE; + gdisplay_check_device_cursor (gdisp); + } + if (active_tool && ((active_tool->type == MOVE) || !gimage_is_empty (gdisp->gimage)) && (mevent->state & GDK_BUTTON1_MASK)) @@ -369,6 +419,8 @@ gdisplay_canvas_events (GtkWidget *canvas, gdisplay_install_tool_cursor (gdisp, GDK_TOP_LEFT_ARROW); } + gdisplay_update_cursor (gdisp, tx, ty); + return return_val; } diff --git a/app/display/gimpdisplayshell-callbacks.h b/app/display/gimpdisplayshell-callbacks.h index 2d7089095e..d7e4246063 100644 --- a/app/display/gimpdisplayshell-callbacks.h +++ b/app/display/gimpdisplayshell-callbacks.h @@ -21,7 +21,9 @@ #define CANVAS_EVENT_MASK GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | \ GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | \ GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK | \ - GDK_ENTER_NOTIFY_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK + GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | \ + GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | \ + GDK_PROXIMITY_OUT_MASK gint gdisplay_canvas_events (GtkWidget *, GdkEvent *); gint gdisplay_hruler_button_press (GtkWidget *, GdkEventButton *, gpointer); diff --git a/app/display/gimpdisplayshell-draw.c b/app/display/gimpdisplayshell-draw.c index 0de8cf6fda..3cd05f5e36 100644 --- a/app/display/gimpdisplayshell-draw.c +++ b/app/display/gimpdisplayshell-draw.c @@ -23,6 +23,7 @@ #include "colormaps.h" #include "color_area.h" #include "commands.h" +#include "devices.h" #include "disp_callbacks.h" #include "errors.h" #include "gdisplay.h" @@ -54,6 +55,9 @@ static void toolbox_destroy (void); static gint toolbox_delete (GtkWidget *, GdkEvent *, gpointer); +static gint toolbox_check_device (GtkWidget *, + GdkEvent *, + gpointer); static GdkPixmap *create_pixmap (GdkWindow *parent, GdkBitmap **mask, @@ -238,6 +242,14 @@ toolbox_destroy () app_exit_finish (); } +static gint +toolbox_check_device (GtkWidget *w, GdkEvent *e, gpointer data) +{ + devices_check_change (e); + + return FALSE; +} + static void gdisplay_destroy (GtkWidget *w, GDisplay *gdisp) @@ -327,6 +339,28 @@ create_color_area (GtkWidget *parent) } +GdkPixmap * +create_tool_pixmap (GtkWidget *parent, ToolType type) +{ + int i; + + if (type == SCALE || type == SHEAR || type == PERSPECTIVE) + type = ROTATE; + else if (type == FLIP_VERT) + type = FLIP_HORZ; + + for (i=0; i<21; i++) + { + if ((ToolType)tool_data[i].callback_data == type) + return create_pixmap (parent->window, NULL, + tool_data[i].icon_data, 22, 22); + } + + g_return_val_if_fail (FALSE, NULL); + + return NULL; /* not reached */ +} + static void create_tools (GtkWidget *parent) { @@ -499,6 +533,7 @@ create_toolbox () GtkWidget *main_vbox; GtkWidget *vbox; GtkWidget *menubar; + GList *device_list; GtkAcceleratorTable *table; window = gtk_window_new (GTK_WINDOW_TOPLEVEL); @@ -513,6 +548,31 @@ create_toolbox () (GtkSignalFunc) toolbox_destroy, NULL); + /* We need to know when the current device changes, so we can update + * the correct tool - to do this we connect to motion events. + * We can't just use EXTENSION_EVENTS_CURSOR though, since that + * would get us extension events for the mouse pointer, and our + * device would change to that and not change back. So we check + * manually that all devices have a cursor, before establishing the check. + */ + device_list = gdk_input_list_devices (); + while (device_list) + { + if (!((GdkDeviceInfo *)(device_list->data))->has_cursor) + break; + + device_list = device_list->next; + } + + if (!device_list) /* all devices have cursor */ + { + gtk_signal_connect (GTK_OBJECT (window), "motion_notify_event", + GTK_SIGNAL_FUNC (toolbox_check_device), NULL); + + gtk_widget_set_events (window, GDK_POINTER_MOTION_MASK); + gtk_widget_set_extension_events (window, GDK_EXTENSION_EVENTS_CURSOR); + } + main_vbox = gtk_vbox_new (FALSE, 1); gtk_container_border_width (GTK_CONTAINER (main_vbox), 1); gtk_container_add (GTK_CONTAINER (window), main_vbox); @@ -682,6 +742,7 @@ create_display_shell (int gdisp_id, gdisp->canvas = gtk_drawing_area_new (); gtk_drawing_area_size (GTK_DRAWING_AREA (gdisp->canvas), n_width, n_height); gtk_widget_set_events (gdisp->canvas, CANVAS_EVENT_MASK); + gtk_widget_set_extension_events (gdisp->canvas, GDK_EXTENSION_EVENTS_ALL); GTK_WIDGET_SET_FLAGS (gdisp->canvas, GTK_CAN_FOCUS); gtk_signal_connect (GTK_OBJECT (gdisp->canvas), "event", (GtkSignalFunc) gdisplay_canvas_events, diff --git a/app/display/gimpdisplayshell-draw.h b/app/display/gimpdisplayshell-draw.h index 8e52dc4e0e..f14b3dbbbc 100644 --- a/app/display/gimpdisplayshell-draw.h +++ b/app/display/gimpdisplayshell-draw.h @@ -18,6 +18,8 @@ #ifndef __INTERFACE_H__ #define __INTERFACE_H__ +#include "tools.h" + /* typedefs */ typedef void (*QueryFunc) (GtkWidget *, gpointer, gpointer); @@ -28,6 +30,7 @@ extern GtkTooltips *tool_tips; /* function declarations */ GtkWidget * create_pixmap_widget (GdkWindow *, char **, int, int); +GdkPixmap * create_tool_pixmap (GtkWidget *, ToolType); void create_toolbox (void); void toolbox_free (void); void toolbox_raise_callback (GtkWidget *, gpointer); diff --git a/app/display/gimpdisplayshell-scroll.c b/app/display/gimpdisplayshell-scroll.c index 328b947bf8..2690e3aa18 100644 --- a/app/display/gimpdisplayshell-scroll.c +++ b/app/display/gimpdisplayshell-scroll.c @@ -102,7 +102,7 @@ void scroll_to_pointer_position (GDisplay *gdisp, GdkEventMotion *mevent) { - int child_x, child_y; + double child_x, child_y; int off_x, off_y; off_x = off_y = 0; @@ -119,7 +119,9 @@ scroll_to_pointer_position (GDisplay *gdisp, if (scroll_display (gdisp, off_x, off_y)) { - gdk_window_get_pointer (gdisp->canvas->window, &child_x, &child_y, NULL); + gdk_input_window_get_pointer (gdisp->canvas->window, mevent->deviceid, + &child_x, &child_y, + NULL, NULL, NULL, NULL); if (child_x == mevent->x && child_y == mevent->y) /* Put this event back on the queue -- so it keeps scrolling */ diff --git a/app/display/gimpdisplayshell.c b/app/display/gimpdisplayshell.c index 0de8cf6fda..3cd05f5e36 100644 --- a/app/display/gimpdisplayshell.c +++ b/app/display/gimpdisplayshell.c @@ -23,6 +23,7 @@ #include "colormaps.h" #include "color_area.h" #include "commands.h" +#include "devices.h" #include "disp_callbacks.h" #include "errors.h" #include "gdisplay.h" @@ -54,6 +55,9 @@ static void toolbox_destroy (void); static gint toolbox_delete (GtkWidget *, GdkEvent *, gpointer); +static gint toolbox_check_device (GtkWidget *, + GdkEvent *, + gpointer); static GdkPixmap *create_pixmap (GdkWindow *parent, GdkBitmap **mask, @@ -238,6 +242,14 @@ toolbox_destroy () app_exit_finish (); } +static gint +toolbox_check_device (GtkWidget *w, GdkEvent *e, gpointer data) +{ + devices_check_change (e); + + return FALSE; +} + static void gdisplay_destroy (GtkWidget *w, GDisplay *gdisp) @@ -327,6 +339,28 @@ create_color_area (GtkWidget *parent) } +GdkPixmap * +create_tool_pixmap (GtkWidget *parent, ToolType type) +{ + int i; + + if (type == SCALE || type == SHEAR || type == PERSPECTIVE) + type = ROTATE; + else if (type == FLIP_VERT) + type = FLIP_HORZ; + + for (i=0; i<21; i++) + { + if ((ToolType)tool_data[i].callback_data == type) + return create_pixmap (parent->window, NULL, + tool_data[i].icon_data, 22, 22); + } + + g_return_val_if_fail (FALSE, NULL); + + return NULL; /* not reached */ +} + static void create_tools (GtkWidget *parent) { @@ -499,6 +533,7 @@ create_toolbox () GtkWidget *main_vbox; GtkWidget *vbox; GtkWidget *menubar; + GList *device_list; GtkAcceleratorTable *table; window = gtk_window_new (GTK_WINDOW_TOPLEVEL); @@ -513,6 +548,31 @@ create_toolbox () (GtkSignalFunc) toolbox_destroy, NULL); + /* We need to know when the current device changes, so we can update + * the correct tool - to do this we connect to motion events. + * We can't just use EXTENSION_EVENTS_CURSOR though, since that + * would get us extension events for the mouse pointer, and our + * device would change to that and not change back. So we check + * manually that all devices have a cursor, before establishing the check. + */ + device_list = gdk_input_list_devices (); + while (device_list) + { + if (!((GdkDeviceInfo *)(device_list->data))->has_cursor) + break; + + device_list = device_list->next; + } + + if (!device_list) /* all devices have cursor */ + { + gtk_signal_connect (GTK_OBJECT (window), "motion_notify_event", + GTK_SIGNAL_FUNC (toolbox_check_device), NULL); + + gtk_widget_set_events (window, GDK_POINTER_MOTION_MASK); + gtk_widget_set_extension_events (window, GDK_EXTENSION_EVENTS_CURSOR); + } + main_vbox = gtk_vbox_new (FALSE, 1); gtk_container_border_width (GTK_CONTAINER (main_vbox), 1); gtk_container_add (GTK_CONTAINER (window), main_vbox); @@ -682,6 +742,7 @@ create_display_shell (int gdisp_id, gdisp->canvas = gtk_drawing_area_new (); gtk_drawing_area_size (GTK_DRAWING_AREA (gdisp->canvas), n_width, n_height); gtk_widget_set_events (gdisp->canvas, CANVAS_EVENT_MASK); + gtk_widget_set_extension_events (gdisp->canvas, GDK_EXTENSION_EVENTS_ALL); GTK_WIDGET_SET_FLAGS (gdisp->canvas, GTK_CAN_FOCUS); gtk_signal_connect (GTK_OBJECT (gdisp->canvas), "event", (GtkSignalFunc) gdisplay_canvas_events, diff --git a/app/display/gimpdisplayshell.h b/app/display/gimpdisplayshell.h index 8e52dc4e0e..f14b3dbbbc 100644 --- a/app/display/gimpdisplayshell.h +++ b/app/display/gimpdisplayshell.h @@ -18,6 +18,8 @@ #ifndef __INTERFACE_H__ #define __INTERFACE_H__ +#include "tools.h" + /* typedefs */ typedef void (*QueryFunc) (GtkWidget *, gpointer, gpointer); @@ -28,6 +30,7 @@ extern GtkTooltips *tool_tips; /* function declarations */ GtkWidget * create_pixmap_widget (GdkWindow *, char **, int, int); +GdkPixmap * create_tool_pixmap (GtkWidget *, ToolType); void create_toolbox (void); void toolbox_free (void); void toolbox_raise_callback (GtkWidget *, gpointer); diff --git a/app/gdisplay.c b/app/gdisplay.c index 8a0203361e..503ab3c4bd 100644 --- a/app/gdisplay.c +++ b/app/gdisplay.c @@ -75,6 +75,7 @@ static GSList * gdisplay_process_area_list(GSList *, GArea *); static void gdisplay_add_update_area (GDisplay *, int, int, int, int); static void gdisplay_add_display_area (GDisplay *, int, int, int, int); static void gdisplay_paint_area (GDisplay *, int, int, int, int); +static void gdisplay_draw_cursor (GDisplay *); static void gdisplay_display_area (GDisplay *, int, int, int, int); static guint gdisplay_hash (GDisplay *); @@ -121,6 +122,10 @@ gdisplay_new (GImage *gimage, gdisp->draw_guides = TRUE; gdisp->snap_to_guides = TRUE; + gdisp->draw_cursor = FALSE; + gdisp->proximity = FALSE; + gdisp->have_cursor = FALSE; + /* add the new display to the list so that it isn't lost */ display_list = g_slist_append (display_list, (void *) gdisp); @@ -340,6 +345,10 @@ gdisplay_flush (GDisplay *gdisp) /* draw the guides */ gdisplay_draw_guides (gdisp); + /* and the cursor (if we have a software cursor */ + if (gdisp->have_cursor) + gdisplay_draw_cursor (gdisp); + /* restart (and recalculate) the selection boundaries */ selection_start (gdisp->select, TRUE); @@ -520,10 +529,10 @@ gdisplay_find_guide (GDisplay *gdisp, void gdisplay_snap_point (GDisplay *gdisp, - int x , - int y, - int *tx, - int *ty) + gdouble x , + gdouble y, + gdouble *tx, + gdouble *ty) { GList *tmp_list; Guide *guide; @@ -598,8 +607,8 @@ gdisplay_snap_rectangle (GDisplay *gdisp, int *tx1, int *ty1) { - int nx1, ny1; - int nx2, ny2; + double nx1, ny1; + double nx2, ny2; *tx1 = x1; *ty1 = y1; @@ -611,18 +620,73 @@ gdisplay_snap_rectangle (GDisplay *gdisp, gdisplay_snap_point (gdisp, x1, y1, &nx1, &ny1); gdisplay_snap_point (gdisp, x2, y2, &nx2, &ny2); - if (x1 != nx1) + if (x1 != (int)nx1) *tx1 = nx1; - else if (x2 != nx2) + else if (x2 != (int)nx2) *tx1 = x1 + (nx2 - x2); - if (y1 != ny1) + if (y1 != (int)ny1) *ty1 = ny1; - else if (y2 != ny2) + else if (y2 != (int)ny2) *ty1 = y1 + (ny2 - y2); } } +void +gdisplay_draw_cursor (GDisplay *gdisp) +{ + int x = gdisp->cursor_x; + int y = gdisp->cursor_y; + + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->white_gc, + x - 7, y-1, x + 7, y-1); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->black_gc, + x - 7, y, x + 7, y); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->white_gc, + x - 7, y+1, x + 7, y+1); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->white_gc, + x-1, y - 7, x-1, y + 7); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->black_gc, + x, y - 7, x, y + 7); + gdk_draw_line (gdisp->canvas->window, + gdisp->canvas->style->white_gc, + x+1, y - 7, x+1, y + 7); +} + +void +gdisplay_update_cursor (GDisplay *gdisp, int x, int y) +{ + int new_cursor; + + new_cursor = gdisp->draw_cursor && gdisp->proximity; + + /* Erase old cursor, if necessary */ + + if (gdisp->have_cursor && (!new_cursor || x != gdisp->cursor_x || + y != gdisp->cursor_y)) + { + gdisplay_expose_area (gdisp, gdisp->cursor_x - 7, + gdisp->cursor_y - 7, + 15, 15); + if (!new_cursor) + { + gdisp->have_cursor = FALSE; + gdisplay_flush (gdisp); + } + } + + gdisp->have_cursor = new_cursor; + gdisp->cursor_x = x; + gdisp->cursor_y = y; + + if (new_cursor) + gdisplay_flush (gdisp); +} void gdisplay_remove_and_delete (GDisplay *gdisp) diff --git a/app/gdisplay.h b/app/gdisplay.h index 575099fc93..926413ca4f 100644 --- a/app/gdisplay.h +++ b/app/gdisplay.h @@ -89,6 +89,12 @@ struct _GDisplay GSList *display_areas; /* Display areas list */ GdkCursorType current_cursor; /* Currently installed cursor */ + + short draw_cursor; /* should we draw software cursor ? */ + int cursor_x; /* software cursor X value */ + int cursor_y; /* software cursor Y value */ + short proximity; /* is a device in proximity of gdisplay ? */ + short have_cursor; /* is cursor currently drawn ? */ }; @@ -116,8 +122,9 @@ void gdisplay_flush (GDisplay *); void gdisplay_draw_guides (GDisplay *); void gdisplay_draw_guide (GDisplay *, Guide *, int); Guide* gdisplay_find_guide (GDisplay *, int, int); -void gdisplay_snap_point (GDisplay *, int , int, int *, int *); +void gdisplay_snap_point (GDisplay *, double , double, double *, double *); void gdisplay_snap_rectangle (GDisplay *, int, int, int, int, int *, int *); +void gdisplay_update_cursor (GDisplay *, int, int); /* function declarations */ diff --git a/app/gimprc.c b/app/gimprc.c index de471d4cda..d491553ca4 100644 --- a/app/gimprc.c +++ b/app/gimprc.c @@ -25,6 +25,7 @@ #include "app_procs.h" #include "appenv.h" +#include "devices.h" #include "errors.h" #include "fileops.h" #include "general.h" @@ -32,6 +33,7 @@ #include "menus.h" #include "plug_in.h" #include "gimage.h" +#include "tools.h" #define ERROR 0 #define DONE 1 @@ -51,7 +53,8 @@ typedef enum { TT_XRULERUNIT, TT_XPLUGIN, TT_XPLUGINDEF, - TT_XMENUPATH + TT_XMENUPATH, + TT_XDEVICE } TokenType; typedef struct _ParseFunc ParseFunc; @@ -133,6 +136,7 @@ static int parse_preview_size (gpointer val1p, gpointer val2p); static int parse_ruler_units (gpointer val1p, gpointer val2p); static int parse_plug_in (gpointer val1p, gpointer val2p); static int parse_plug_in_def (gpointer val1p, gpointer val2p); +static int parse_device (gpointer val1p, gpointer val2p); static int parse_menu_path (gpointer val1p, gpointer val2p); static int parse_proc_def (PlugInProcDef **proc_def); @@ -218,6 +222,7 @@ static ParseFunc funcs[] = { "plug-in", TT_XPLUGIN, NULL, NULL }, { "plug-in-def", TT_XPLUGINDEF, NULL, NULL }, { "menu-path", TT_XMENUPATH, NULL, NULL }, + { "device", TT_XDEVICE, NULL, NULL }, { "show-tool-tips", TT_BOOLEAN, &show_tool_tips, NULL }, { "dont-show-tool-tips", TT_BOOLEAN, NULL, &show_tool_tips }, }; @@ -570,6 +575,8 @@ parse_statement () return parse_plug_in_def (funcs[i].val1p, funcs[i].val2p); case TT_XMENUPATH: return parse_menu_path (funcs[i].val1p, funcs[i].val2p); + case TT_XDEVICE: + return parse_device (funcs[i].val1p, funcs[i].val2p); } return parse_unknown (token_sym); @@ -1491,6 +1498,233 @@ transform_path (char *path, return new_path; } +/* Copied from gtk_menu_factory_parse_accelerator() */ +static void +parse_device_accelerator (const char *accelerator, + GdkDeviceKey *key) +{ + int done; + + g_return_if_fail (accelerator != NULL); + g_return_if_fail (key != NULL); + + key->modifiers = 0; + + done = FALSE; + while (!done) + { + if (strncmp (accelerator, "", 7) == 0) + { + accelerator += 7; + key->modifiers |= GDK_SHIFT_MASK; + } + else if (strncmp (accelerator, "", 5) == 0) + { + accelerator += 5; + key->modifiers |= GDK_MOD1_MASK; + } + else if (strncmp (accelerator, "", 9) == 0) + { + accelerator += 9; + key->modifiers |= GDK_CONTROL_MASK; + } + else + { + done = TRUE; + /* Tricky, but works... ("" => keyval = 0, or no translation) */ + key->keyval = accelerator[0]; + } + } +} + +static int +parse_device (gpointer val1p, + gpointer val2p) +{ + DeviceValues values = 0; + int i; + int token; + + /* The initialized values here are meaningless */ + gchar *name = NULL; + GdkInputMode mode = GDK_MODE_DISABLED; + gint num_axes = 0; + GdkAxisUse *axes = NULL; + gint num_keys = 0; + GdkDeviceKey *keys = NULL; + gchar *brush_name = NULL; + ToolType tool = RECT_SELECT; + guchar foreground[3] = { 0, 0, 0 }; + + token = peek_next_token (); + if (!token || (token != TOKEN_STRING)) + goto error; + token = get_next_token (); + + name = g_strdup (token_str); + + /* Parse options for device */ + + while ( peek_next_token () == TOKEN_LEFT_PAREN ) + { + token = get_next_token (); + + token = peek_next_token (); + if (!token || (token != TOKEN_SYMBOL)) + goto error; + token = get_next_token (); + + if (!strcmp ("mode", token_sym)) + { + values |= DEVICE_MODE; + + token = peek_next_token (); + if (!token || (token != TOKEN_SYMBOL)) + goto error; + token = get_next_token (); + + if (!strcmp ("disabled", token_sym)) + mode = GDK_MODE_DISABLED; + else if (!strcmp ("window", token_sym)) + mode = GDK_MODE_WINDOW; + else if (!strcmp ("screen", token_sym)) + mode = GDK_MODE_SCREEN; + else + goto error; + } + else if (!strcmp ("axes", token_sym)) + { + values |= DEVICE_AXES; + + token = peek_next_token (); + if (!token || (token != TOKEN_NUMBER)) + goto error; + token = get_next_token (); + + num_axes = token_int; + axes = g_new (GdkAxisUse, num_axes); + + for (i=0; i CONVOLVE) + goto error; + + } + else if (!strcmp ("foreground", token_sym)) + { + values |= DEVICE_FOREGROUND; + + for (i=0; i<3; i++) + { + token = peek_next_token (); + if (!token || (token != TOKEN_NUMBER)) + goto error; + token = get_next_token (); + + foreground[i] = token_int; + } + } + else + goto error; + + token = peek_next_token (); + if (!token || (token != TOKEN_RIGHT_PAREN)) + goto error; + token = get_next_token (); + } + + if (!token || (token != TOKEN_RIGHT_PAREN)) + goto error; + token = get_next_token (); + + devices_rc_update (name, values, mode, num_axes, axes, num_keys, keys, + brush_name, tool, foreground); + + g_free (brush_name); + g_free (name); + g_free (axes); + g_free (keys); + + return OK; + +error: + g_free (brush_name); + g_free (name); + g_free (axes); + g_free (keys); + + return ERROR; +} + static int parse_unknown (char *token_sym) { @@ -1578,6 +1812,7 @@ value_to_str (char *name) case TT_XPLUGIN: case TT_XPLUGINDEF: case TT_XMENUPATH: + case TT_XDEVICE: return NULL; } return NULL; diff --git a/app/gui/commands.c b/app/gui/commands.c index ceaf9a3633..8e681932da 100644 --- a/app/gui/commands.c +++ b/app/gui/commands.c @@ -32,6 +32,7 @@ #include "convert.h" #include "curves.h" #include "desaturate.h" +#include "devices.h" #include "channel_ops.h" #include "drawable.h" #include "equalize.h" @@ -2436,6 +2437,20 @@ dialogs_tools_options_cmd_callback (GtkWidget *widget, tools_options_dialog_show (); } +void +dialogs_input_devices_cmd_callback (GtkWidget *widget, + gpointer client_data) +{ + create_input_dialog (); +} + +void +dialogs_device_status_cmd_callback (GtkWidget *widget, + gpointer client_data) +{ + create_device_status (); +} + void about_dialog_cmd_callback (GtkWidget *widget, gpointer client_data) diff --git a/app/gui/commands.h b/app/gui/commands.h index 5ec59cbb85..645262543a 100644 --- a/app/gui/commands.h +++ b/app/gui/commands.h @@ -89,6 +89,8 @@ void dialogs_gradient_editor_cmd_callback (GtkWidget *, gpointer); void dialogs_lc_cmd_callback (GtkWidget *, gpointer); void dialogs_indexed_palette_cmd_callback (GtkWidget *, gpointer); void dialogs_tools_options_cmd_callback (GtkWidget *, gpointer); +void dialogs_input_devices_cmd_callback (GtkWidget *, gpointer); +void dialogs_device_status_cmd_callback (GtkWidget *, gpointer); void about_dialog_cmd_callback (GtkWidget *, gpointer); void tips_dialog_cmd_callback (GtkWidget *, gpointer); diff --git a/app/gui/help-commands.c b/app/gui/help-commands.c index ceaf9a3633..8e681932da 100644 --- a/app/gui/help-commands.c +++ b/app/gui/help-commands.c @@ -32,6 +32,7 @@ #include "convert.h" #include "curves.h" #include "desaturate.h" +#include "devices.h" #include "channel_ops.h" #include "drawable.h" #include "equalize.h" @@ -2436,6 +2437,20 @@ dialogs_tools_options_cmd_callback (GtkWidget *widget, tools_options_dialog_show (); } +void +dialogs_input_devices_cmd_callback (GtkWidget *widget, + gpointer client_data) +{ + create_input_dialog (); +} + +void +dialogs_device_status_cmd_callback (GtkWidget *widget, + gpointer client_data) +{ + create_device_status (); +} + void about_dialog_cmd_callback (GtkWidget *widget, gpointer client_data) diff --git a/app/gui/help-commands.h b/app/gui/help-commands.h index 5ec59cbb85..645262543a 100644 --- a/app/gui/help-commands.h +++ b/app/gui/help-commands.h @@ -89,6 +89,8 @@ void dialogs_gradient_editor_cmd_callback (GtkWidget *, gpointer); void dialogs_lc_cmd_callback (GtkWidget *, gpointer); void dialogs_indexed_palette_cmd_callback (GtkWidget *, gpointer); void dialogs_tools_options_cmd_callback (GtkWidget *, gpointer); +void dialogs_input_devices_cmd_callback (GtkWidget *, gpointer); +void dialogs_device_status_cmd_callback (GtkWidget *, gpointer); void about_dialog_cmd_callback (GtkWidget *, gpointer); void tips_dialog_cmd_callback (GtkWidget *, gpointer); diff --git a/app/gui/menus.c b/app/gui/menus.c index 25ff8baecf..86a0f91e0d 100644 --- a/app/gui/menus.c +++ b/app/gui/menus.c @@ -57,6 +57,8 @@ static GtkMenuEntry menu_items[] = { "/File/Dialogs/Palette...", "P", dialogs_palette_cmd_callback, NULL }, { "/File/Dialogs/Gradient Editor...", "G", dialogs_gradient_editor_cmd_callback, NULL }, { "/File/Dialogs/Tool Options...", "T", dialogs_tools_options_cmd_callback, NULL }, + { "/File/Dialogs/Input Devices...", NULL, dialogs_input_devices_cmd_callback, NULL }, + { "/File/Dialogs/Device Status...", NULL, dialogs_device_status_cmd_callback, NULL }, { "/File/",NULL,NULL,NULL}, @@ -199,6 +201,8 @@ static GtkMenuEntry menu_items[] = { "/Dialogs/Layers & Channels...", "L", dialogs_lc_cmd_callback, NULL }, { "/Dialogs/Indexed Palette...", NULL, dialogs_indexed_palette_cmd_callback, NULL }, { "/Dialogs/Tool Options...", NULL, dialogs_tools_options_cmd_callback, NULL }, + { "/Dialogs/Input Devices...", NULL, dialogs_input_devices_cmd_callback, NULL }, + { "/Dialogs/Device Status...", NULL, dialogs_device_status_cmd_callback, NULL }, { "/Automatic", NULL, file_load_by_extension_callback, NULL }, { "/", NULL, NULL, NULL }, diff --git a/app/gui/palette-editor.c b/app/gui/palette-editor.c index ee1f8afbe5..177a6cad19 100644 --- a/app/gui/palette-editor.c +++ b/app/gui/palette-editor.c @@ -29,6 +29,7 @@ #include "color_area.h" #include "color_select.h" #include "datafiles.h" +#include "devices.h" #include "errors.h" #include "general.h" #include "gimprc.h" @@ -338,6 +339,7 @@ palette_set_foreground (int r, { store_color (&foreground_pixel, rr, gg, bb); color_area_update (); + device_status_update (current_device); } } diff --git a/app/interface.c b/app/interface.c index 0de8cf6fda..3cd05f5e36 100644 --- a/app/interface.c +++ b/app/interface.c @@ -23,6 +23,7 @@ #include "colormaps.h" #include "color_area.h" #include "commands.h" +#include "devices.h" #include "disp_callbacks.h" #include "errors.h" #include "gdisplay.h" @@ -54,6 +55,9 @@ static void toolbox_destroy (void); static gint toolbox_delete (GtkWidget *, GdkEvent *, gpointer); +static gint toolbox_check_device (GtkWidget *, + GdkEvent *, + gpointer); static GdkPixmap *create_pixmap (GdkWindow *parent, GdkBitmap **mask, @@ -238,6 +242,14 @@ toolbox_destroy () app_exit_finish (); } +static gint +toolbox_check_device (GtkWidget *w, GdkEvent *e, gpointer data) +{ + devices_check_change (e); + + return FALSE; +} + static void gdisplay_destroy (GtkWidget *w, GDisplay *gdisp) @@ -327,6 +339,28 @@ create_color_area (GtkWidget *parent) } +GdkPixmap * +create_tool_pixmap (GtkWidget *parent, ToolType type) +{ + int i; + + if (type == SCALE || type == SHEAR || type == PERSPECTIVE) + type = ROTATE; + else if (type == FLIP_VERT) + type = FLIP_HORZ; + + for (i=0; i<21; i++) + { + if ((ToolType)tool_data[i].callback_data == type) + return create_pixmap (parent->window, NULL, + tool_data[i].icon_data, 22, 22); + } + + g_return_val_if_fail (FALSE, NULL); + + return NULL; /* not reached */ +} + static void create_tools (GtkWidget *parent) { @@ -499,6 +533,7 @@ create_toolbox () GtkWidget *main_vbox; GtkWidget *vbox; GtkWidget *menubar; + GList *device_list; GtkAcceleratorTable *table; window = gtk_window_new (GTK_WINDOW_TOPLEVEL); @@ -513,6 +548,31 @@ create_toolbox () (GtkSignalFunc) toolbox_destroy, NULL); + /* We need to know when the current device changes, so we can update + * the correct tool - to do this we connect to motion events. + * We can't just use EXTENSION_EVENTS_CURSOR though, since that + * would get us extension events for the mouse pointer, and our + * device would change to that and not change back. So we check + * manually that all devices have a cursor, before establishing the check. + */ + device_list = gdk_input_list_devices (); + while (device_list) + { + if (!((GdkDeviceInfo *)(device_list->data))->has_cursor) + break; + + device_list = device_list->next; + } + + if (!device_list) /* all devices have cursor */ + { + gtk_signal_connect (GTK_OBJECT (window), "motion_notify_event", + GTK_SIGNAL_FUNC (toolbox_check_device), NULL); + + gtk_widget_set_events (window, GDK_POINTER_MOTION_MASK); + gtk_widget_set_extension_events (window, GDK_EXTENSION_EVENTS_CURSOR); + } + main_vbox = gtk_vbox_new (FALSE, 1); gtk_container_border_width (GTK_CONTAINER (main_vbox), 1); gtk_container_add (GTK_CONTAINER (window), main_vbox); @@ -682,6 +742,7 @@ create_display_shell (int gdisp_id, gdisp->canvas = gtk_drawing_area_new (); gtk_drawing_area_size (GTK_DRAWING_AREA (gdisp->canvas), n_width, n_height); gtk_widget_set_events (gdisp->canvas, CANVAS_EVENT_MASK); + gtk_widget_set_extension_events (gdisp->canvas, GDK_EXTENSION_EVENTS_ALL); GTK_WIDGET_SET_FLAGS (gdisp->canvas, GTK_CAN_FOCUS); gtk_signal_connect (GTK_OBJECT (gdisp->canvas), "event", (GtkSignalFunc) gdisplay_canvas_events, diff --git a/app/interface.h b/app/interface.h index 8e52dc4e0e..f14b3dbbbc 100644 --- a/app/interface.h +++ b/app/interface.h @@ -18,6 +18,8 @@ #ifndef __INTERFACE_H__ #define __INTERFACE_H__ +#include "tools.h" + /* typedefs */ typedef void (*QueryFunc) (GtkWidget *, gpointer, gpointer); @@ -28,6 +30,7 @@ extern GtkTooltips *tool_tips; /* function declarations */ GtkWidget * create_pixmap_widget (GdkWindow *, char **, int, int); +GdkPixmap * create_tool_pixmap (GtkWidget *, ToolType); void create_toolbox (void); void toolbox_free (void); void toolbox_raise_callback (GtkWidget *, gpointer); diff --git a/app/menus.c b/app/menus.c index 25ff8baecf..86a0f91e0d 100644 --- a/app/menus.c +++ b/app/menus.c @@ -57,6 +57,8 @@ static GtkMenuEntry menu_items[] = { "/File/Dialogs/Palette...", "P", dialogs_palette_cmd_callback, NULL }, { "/File/Dialogs/Gradient Editor...", "G", dialogs_gradient_editor_cmd_callback, NULL }, { "/File/Dialogs/Tool Options...", "T", dialogs_tools_options_cmd_callback, NULL }, + { "/File/Dialogs/Input Devices...", NULL, dialogs_input_devices_cmd_callback, NULL }, + { "/File/Dialogs/Device Status...", NULL, dialogs_device_status_cmd_callback, NULL }, { "/File/",NULL,NULL,NULL}, @@ -199,6 +201,8 @@ static GtkMenuEntry menu_items[] = { "/Dialogs/Layers & Channels...", "L", dialogs_lc_cmd_callback, NULL }, { "/Dialogs/Indexed Palette...", NULL, dialogs_indexed_palette_cmd_callback, NULL }, { "/Dialogs/Tool Options...", NULL, dialogs_tools_options_cmd_callback, NULL }, + { "/Dialogs/Input Devices...", NULL, dialogs_input_devices_cmd_callback, NULL }, + { "/Dialogs/Device Status...", NULL, dialogs_device_status_cmd_callback, NULL }, { "/Automatic", NULL, file_load_by_extension_callback, NULL }, { "/", NULL, NULL, NULL }, diff --git a/app/menus/menus.c b/app/menus/menus.c index 25ff8baecf..86a0f91e0d 100644 --- a/app/menus/menus.c +++ b/app/menus/menus.c @@ -57,6 +57,8 @@ static GtkMenuEntry menu_items[] = { "/File/Dialogs/Palette...", "P", dialogs_palette_cmd_callback, NULL }, { "/File/Dialogs/Gradient Editor...", "G", dialogs_gradient_editor_cmd_callback, NULL }, { "/File/Dialogs/Tool Options...", "T", dialogs_tools_options_cmd_callback, NULL }, + { "/File/Dialogs/Input Devices...", NULL, dialogs_input_devices_cmd_callback, NULL }, + { "/File/Dialogs/Device Status...", NULL, dialogs_device_status_cmd_callback, NULL }, { "/File/",NULL,NULL,NULL}, @@ -199,6 +201,8 @@ static GtkMenuEntry menu_items[] = { "/Dialogs/Layers & Channels...", "L", dialogs_lc_cmd_callback, NULL }, { "/Dialogs/Indexed Palette...", NULL, dialogs_indexed_palette_cmd_callback, NULL }, { "/Dialogs/Tool Options...", NULL, dialogs_tools_options_cmd_callback, NULL }, + { "/Dialogs/Input Devices...", NULL, dialogs_input_devices_cmd_callback, NULL }, + { "/Dialogs/Device Status...", NULL, dialogs_device_status_cmd_callback, NULL }, { "/Automatic", NULL, file_load_by_extension_callback, NULL }, { "/", NULL, NULL, NULL }, diff --git a/app/paint_core.c b/app/paint_core.c index 87155ba798..8840f13937 100644 --- a/app/paint_core.c +++ b/app/paint_core.c @@ -40,6 +40,7 @@ PaintCore non_gui_paint_core; /* local function prototypes */ static MaskBuf * paint_core_subsample_mask (MaskBuf *, double, double); +static MaskBuf * paint_core_pressurize_mask (MaskBuf *, double, double, double); static MaskBuf * paint_core_solidify_mask (MaskBuf *); static MaskBuf * paint_core_get_brush_mask (PaintCore *, int); static void paint_core_paste (PaintCore *, MaskBuf *, GimpDrawable *, int, int, int, int); @@ -67,6 +68,7 @@ static TempBuf * canvas_buf = NULL; /* brush buffers */ +static MaskBuf * pressure_brush; static MaskBuf * solid_brush; static MaskBuf * kernel_brushes[5][5]; @@ -141,6 +143,9 @@ paint_core_button_press (tool, bevent, gdisp_ptr) if (! paint_core_init (paint_core, drawable, x, y)) return; + paint_core->curpressure = bevent->pressure; + paint_core->curxtilt = bevent->xtilt; + paint_core->curytilt = bevent->ytilt; paint_core->state = bevent->state; /* if this is a new image, reinit the core vals */ @@ -150,6 +155,9 @@ paint_core_button_press (tool, bevent, gdisp_ptr) /* initialize some values */ paint_core->startx = paint_core->lastx = paint_core->curx; paint_core->starty = paint_core->lasty = paint_core->cury; + paint_core->startpressure = paint_core->lastpressure = paint_core->curpressure; + paint_core->startytilt = paint_core->lastytilt = paint_core->curytilt; + paint_core->startxtilt = paint_core->lastxtilt = paint_core->curxtilt; } /* If shift is down and this is not the first paint * stroke, then draw a line from the last coords to the pointer @@ -159,6 +167,9 @@ paint_core_button_press (tool, bevent, gdisp_ptr) draw_line = 1; paint_core->startx = paint_core->lastx; paint_core->starty = paint_core->lasty; + paint_core->startpressure = paint_core->lastpressure; + paint_core->startxtilt = paint_core->lastxtilt; + paint_core->startytilt = paint_core->lastytilt; } tool->state = ACTIVE; @@ -187,6 +198,9 @@ paint_core_button_press (tool, bevent, gdisp_ptr) paint_core_interpolate (paint_core, drawable); paint_core->lastx = paint_core->curx; paint_core->lasty = paint_core->cury; + paint_core->lastpressure = paint_core->curpressure; + paint_core->lastxtilt = paint_core->curxtilt; + paint_core->lastytilt = paint_core->curytilt; } else (* paint_core->paint_func) (paint_core, drawable, MOTION_PAINT); @@ -238,6 +252,9 @@ paint_core_motion (tool, mevent, gdisp_ptr) gdisplay_untransform_coords_f (gdisp, (double) mevent->x, (double) mevent->y, &paint_core->curx, &paint_core->cury, TRUE); + paint_core->curpressure = mevent->pressure; + paint_core->curxtilt = mevent->xtilt; + paint_core->curytilt = mevent->ytilt; paint_core->state = mevent->state; paint_core_interpolate (paint_core, gimage_active_drawable (gdisp->gimage)); @@ -246,6 +263,9 @@ paint_core_motion (tool, mevent, gdisp_ptr) paint_core->lastx = paint_core->curx; paint_core->lasty = paint_core->cury; + paint_core->lastpressure = paint_core->curpressure; + paint_core->lastxtilt = paint_core->curxtilt; + paint_core->lastytilt = paint_core->curytilt; } void @@ -382,6 +402,11 @@ paint_core_init (paint_core, drawable, x, y) paint_core->curx = x; paint_core->cury = y; + /* Set up some defaults for non-gui use */ + paint_core->startpressure = paint_core->lastpressure = paint_core->curpressure = 0.5; + paint_core->startxtilt = paint_core->lastxtilt = paint_core->curxtilt = 0; + paint_core->startytilt = paint_core->lastytilt = paint_core->curytilt = 0; + /* Each buffer is the same size as the maximum bounds of the active brush... */ if (!(brush = get_active_brush ())) { @@ -425,7 +450,7 @@ paint_core_interpolate (paint_core, drawable) GimpDrawable *drawable; { int n; - double dx, dy; + double dx, dy, dpressure, dxtilt, dytilt; double left; double t; double initial; @@ -434,8 +459,11 @@ paint_core_interpolate (paint_core, drawable) dx = paint_core->curx - paint_core->lastx; dy = paint_core->cury - paint_core->lasty; + dpressure = paint_core->curpressure - paint_core->lastpressure; + dxtilt = paint_core->curxtilt - paint_core->lastxtilt; + dytilt = paint_core->curytilt - paint_core->lastytilt; - if (!dx && !dy) + if (!dx && !dy && !dpressure && !dxtilt && !dytilt) return; dist = sqrt (SQR (dx) + SQR (dy)); @@ -454,6 +482,9 @@ paint_core_interpolate (paint_core, drawable) paint_core->curx = paint_core->lastx + dx * t; paint_core->cury = paint_core->lasty + dy * t; + paint_core->curpressure = paint_core->lastpressure + dpressure * t; + paint_core->curxtilt = paint_core->lastxtilt + dxtilt * t; + paint_core->curytilt = paint_core->lastytilt + dytilt * t; (* paint_core->paint_func) (paint_core, drawable, MOTION_PAINT); } } @@ -461,6 +492,9 @@ paint_core_interpolate (paint_core, drawable) paint_core->distance = total; paint_core->curx = paint_core->lastx + dx; paint_core->cury = paint_core->lasty + dy; + paint_core->curpressure = paint_core->lastpressure + dpressure; + paint_core->curxtilt = paint_core->lastxtilt + dxtilt; + paint_core->curytilt = paint_core->lastytilt + dytilt; } void @@ -478,6 +512,7 @@ paint_core_finish (paint_core, drawable, tool_id) /* Determine if any part of the image has been altered-- * if nothing has, then just return... */ + if ((paint_core->x2 == paint_core->x1) || (paint_core->y2 == paint_core->y1)) return; @@ -487,6 +522,9 @@ paint_core_finish (paint_core, drawable, tool_id) pu->tool_ID = tool_id; pu->lastx = paint_core->startx; pu->lasty = paint_core->starty; + pu->lastpressure = paint_core->startpressure; + pu->lastxtilt = paint_core->startxtilt; + pu->lastytilt = paint_core->startytilt; /* Push a paint undo */ undo_push_paint (gimage, pu); @@ -742,6 +780,95 @@ paint_core_subsample_mask (mask, x, y) return dest; } +static MaskBuf * +paint_core_pressurize_mask (brush_mask, x, y, pressure) + MaskBuf * brush_mask; + double x, y; + double pressure; +{ + static MaskBuf *last_brush = NULL; + static double map[256]; + static unsigned char mapi[256]; + unsigned char *source; + unsigned char *dest; + MaskBuf *subsample_mask; + int i; + double ds,s,c; + + /* Get the raw subsampled mask */ + subsample_mask = paint_core_subsample_mask(brush_mask, x, y); + + /* Special case pressure = 0.5 */ + if ((int)(pressure*100+0.5) == 50) + return subsample_mask; + + /* Make sure we have the right sized buffer */ + if (brush_mask != last_brush) + { + if (pressure_brush) + mask_buf_free (pressure_brush); + pressure_brush = mask_buf_new (brush_mask->width + 2, + brush_mask->height +2); + } + + /* Create the pressure profile + + It is: I'(I) = tanh(20*(pressure-0.5)*I) : pressure > 0.5 + I'(I) = 1 - tanh(20*(0.5-pressure)*(1-I)) : pressure < 0.5 + + It looks like: + + low pressure medium pressure high pressure + + | / -- + | / / + / / | + -- / | + + */ + + ds = (pressure - 0.5)*(20./256.); + s = 0; + c = 1.0; + + if (ds > 0) + { + for (i=0;i<256;i++) + { + map[i] = s/c; + s += c*ds; + c += s*ds; + } + for (i=0;i<256;i++) + mapi[i] = (int)(255*map[i]/map[255]); + } + else + { + ds = -ds; + for (i=255;i>=0;i--) + { + map[i] = s/c; + s += c*ds; + c += s*ds; + } + for (i=0;i<256;i++) + mapi[i] = (int)(255*(1-map[i]/map[0])); + } + + /* Now convert the brush */ + + source = mask_buf_data (subsample_mask); + dest = mask_buf_data (pressure_brush); + + i = subsample_mask->width * subsample_mask->height; + while (i--) + { + *dest++ = mapi[(*source++)]; + } + + return pressure_brush; +} + static MaskBuf * paint_core_solidify_mask (brush_mask) MaskBuf * brush_mask; @@ -790,6 +917,9 @@ paint_core_get_brush_mask (paint_core, brush_hardness) case HARD: bm = paint_core_solidify_mask (paint_core->brush_mask); break; + case PRESSURE: + bm = paint_core_pressurize_mask (paint_core->brush_mask, paint_core->curx, paint_core->cury, paint_core->curpressure); + break; default: bm = NULL; break; diff --git a/app/paint_core.h b/app/paint_core.h index 0ed4938534..0d73a98416 100644 --- a/app/paint_core.h +++ b/app/paint_core.h @@ -31,6 +31,7 @@ /* brush application types */ #define HARD 0 /* pencil */ #define SOFT 1 /* paintbrush */ +#define PRESSURE 2 /* paintbrush with variable pressure */ /* paint application modes */ #define CONSTANT 0 /* pencil, paintbrush, airbrush, clone */ @@ -44,12 +45,21 @@ struct _paint_core double startx; /* starting x coord */ double starty; /* starting y coord */ + double startpressure; /* starting pressure */ + double startxtilt; /* starting xtilt */ + double startytilt; /* starting ytilt */ double curx; /* current x coord */ double cury; /* current y coord */ + double curpressure; /* current pressure */ + double curxtilt; /* current xtilt */ + double curytilt; /* current ytilt */ double lastx; /* last x coord */ double lasty; /* last y coord */ + double lastpressure; /* last pressure */ + double lastxtilt; /* last xtilt */ + double lastytilt; /* last ytilt */ int state; /* state of buttons and keys */ @@ -74,6 +84,9 @@ struct _paint_undo int tool_ID; double lastx; double lasty; + double lastpressure; + double lastxtilt; + double lastytilt; }; /* paint tool action functions */ diff --git a/app/paintbrush.c b/app/paintbrush.c index 6bdbec0207..42d0b9079a 100644 --- a/app/paintbrush.c +++ b/app/paintbrush.c @@ -224,7 +224,7 @@ paintbrush_motion (PaintCore *paint_core, /* paste the newly painted canvas to the gimage which is being worked on */ paint_core_paste_canvas (paint_core, drawable, blend, (int) (get_brush_opacity () * 255), - get_brush_paint_mode (), SOFT, + get_brush_paint_mode (), PRESSURE, incremental ? INCREMENTAL : CONSTANT); } } diff --git a/app/palette.c b/app/palette.c index ee1f8afbe5..177a6cad19 100644 --- a/app/palette.c +++ b/app/palette.c @@ -29,6 +29,7 @@ #include "color_area.h" #include "color_select.h" #include "datafiles.h" +#include "devices.h" #include "errors.h" #include "general.h" #include "gimprc.h" @@ -338,6 +339,7 @@ palette_set_foreground (int r, { store_color (&foreground_pixel, rr, gg, bb); color_area_update (); + device_status_update (current_device); } } diff --git a/app/scroll.c b/app/scroll.c index 328b947bf8..2690e3aa18 100644 --- a/app/scroll.c +++ b/app/scroll.c @@ -102,7 +102,7 @@ void scroll_to_pointer_position (GDisplay *gdisp, GdkEventMotion *mevent) { - int child_x, child_y; + double child_x, child_y; int off_x, off_y; off_x = off_y = 0; @@ -119,7 +119,9 @@ scroll_to_pointer_position (GDisplay *gdisp, if (scroll_display (gdisp, off_x, off_y)) { - gdk_window_get_pointer (gdisp->canvas->window, &child_x, &child_y, NULL); + gdk_input_window_get_pointer (gdisp->canvas->window, mevent->deviceid, + &child_x, &child_y, + NULL, NULL, NULL, NULL); if (child_x == mevent->x && child_y == mevent->y) /* Put this event back on the queue -- so it keeps scrolling */ diff --git a/app/tools.c b/app/tools.c index 1528eab3ef..993df27c79 100644 --- a/app/tools.c +++ b/app/tools.c @@ -31,6 +31,7 @@ #include "crop.h" #include "cursorutil.h" #include "curves.h" +#include "devices.h" #include "eraser.h" #include "gdisplay.h" #include "hue_saturation.h" @@ -58,12 +59,12 @@ Tool * active_tool = NULL; Layer * active_tool_layer = NULL; +ToolType active_tool_type = -1; /* Local Data */ static GtkWidget *options_shell = NULL; static GtkWidget *options_vbox = NULL; -static ToolType active_tool_type = -1; static int global_tool_ID = 0; @@ -362,6 +363,10 @@ tools_select (ToolType type) gtk_container_enable_resize (GTK_CONTAINER (options_shell)); + /* Update the device-information dialog */ + + device_status_update (current_device); + /* Set the paused count variable to 0 */ active_tool->paused_count = 0; @@ -583,7 +588,6 @@ tools_options_dialog_new () gtk_widget_show (options_vbox); } - void tools_options_dialog_show () { diff --git a/app/tools.h b/app/tools.h index 7643a13804..c253d16d88 100644 --- a/app/tools.h +++ b/app/tools.h @@ -135,6 +135,7 @@ struct _ToolInfo /* Global Data Structure */ extern Tool * active_tool; +extern ToolType active_tool_type; extern Layer * active_tool_layer; extern ToolInfo tool_info[]; diff --git a/app/tools/paint_core.c b/app/tools/paint_core.c index 87155ba798..8840f13937 100644 --- a/app/tools/paint_core.c +++ b/app/tools/paint_core.c @@ -40,6 +40,7 @@ PaintCore non_gui_paint_core; /* local function prototypes */ static MaskBuf * paint_core_subsample_mask (MaskBuf *, double, double); +static MaskBuf * paint_core_pressurize_mask (MaskBuf *, double, double, double); static MaskBuf * paint_core_solidify_mask (MaskBuf *); static MaskBuf * paint_core_get_brush_mask (PaintCore *, int); static void paint_core_paste (PaintCore *, MaskBuf *, GimpDrawable *, int, int, int, int); @@ -67,6 +68,7 @@ static TempBuf * canvas_buf = NULL; /* brush buffers */ +static MaskBuf * pressure_brush; static MaskBuf * solid_brush; static MaskBuf * kernel_brushes[5][5]; @@ -141,6 +143,9 @@ paint_core_button_press (tool, bevent, gdisp_ptr) if (! paint_core_init (paint_core, drawable, x, y)) return; + paint_core->curpressure = bevent->pressure; + paint_core->curxtilt = bevent->xtilt; + paint_core->curytilt = bevent->ytilt; paint_core->state = bevent->state; /* if this is a new image, reinit the core vals */ @@ -150,6 +155,9 @@ paint_core_button_press (tool, bevent, gdisp_ptr) /* initialize some values */ paint_core->startx = paint_core->lastx = paint_core->curx; paint_core->starty = paint_core->lasty = paint_core->cury; + paint_core->startpressure = paint_core->lastpressure = paint_core->curpressure; + paint_core->startytilt = paint_core->lastytilt = paint_core->curytilt; + paint_core->startxtilt = paint_core->lastxtilt = paint_core->curxtilt; } /* If shift is down and this is not the first paint * stroke, then draw a line from the last coords to the pointer @@ -159,6 +167,9 @@ paint_core_button_press (tool, bevent, gdisp_ptr) draw_line = 1; paint_core->startx = paint_core->lastx; paint_core->starty = paint_core->lasty; + paint_core->startpressure = paint_core->lastpressure; + paint_core->startxtilt = paint_core->lastxtilt; + paint_core->startytilt = paint_core->lastytilt; } tool->state = ACTIVE; @@ -187,6 +198,9 @@ paint_core_button_press (tool, bevent, gdisp_ptr) paint_core_interpolate (paint_core, drawable); paint_core->lastx = paint_core->curx; paint_core->lasty = paint_core->cury; + paint_core->lastpressure = paint_core->curpressure; + paint_core->lastxtilt = paint_core->curxtilt; + paint_core->lastytilt = paint_core->curytilt; } else (* paint_core->paint_func) (paint_core, drawable, MOTION_PAINT); @@ -238,6 +252,9 @@ paint_core_motion (tool, mevent, gdisp_ptr) gdisplay_untransform_coords_f (gdisp, (double) mevent->x, (double) mevent->y, &paint_core->curx, &paint_core->cury, TRUE); + paint_core->curpressure = mevent->pressure; + paint_core->curxtilt = mevent->xtilt; + paint_core->curytilt = mevent->ytilt; paint_core->state = mevent->state; paint_core_interpolate (paint_core, gimage_active_drawable (gdisp->gimage)); @@ -246,6 +263,9 @@ paint_core_motion (tool, mevent, gdisp_ptr) paint_core->lastx = paint_core->curx; paint_core->lasty = paint_core->cury; + paint_core->lastpressure = paint_core->curpressure; + paint_core->lastxtilt = paint_core->curxtilt; + paint_core->lastytilt = paint_core->curytilt; } void @@ -382,6 +402,11 @@ paint_core_init (paint_core, drawable, x, y) paint_core->curx = x; paint_core->cury = y; + /* Set up some defaults for non-gui use */ + paint_core->startpressure = paint_core->lastpressure = paint_core->curpressure = 0.5; + paint_core->startxtilt = paint_core->lastxtilt = paint_core->curxtilt = 0; + paint_core->startytilt = paint_core->lastytilt = paint_core->curytilt = 0; + /* Each buffer is the same size as the maximum bounds of the active brush... */ if (!(brush = get_active_brush ())) { @@ -425,7 +450,7 @@ paint_core_interpolate (paint_core, drawable) GimpDrawable *drawable; { int n; - double dx, dy; + double dx, dy, dpressure, dxtilt, dytilt; double left; double t; double initial; @@ -434,8 +459,11 @@ paint_core_interpolate (paint_core, drawable) dx = paint_core->curx - paint_core->lastx; dy = paint_core->cury - paint_core->lasty; + dpressure = paint_core->curpressure - paint_core->lastpressure; + dxtilt = paint_core->curxtilt - paint_core->lastxtilt; + dytilt = paint_core->curytilt - paint_core->lastytilt; - if (!dx && !dy) + if (!dx && !dy && !dpressure && !dxtilt && !dytilt) return; dist = sqrt (SQR (dx) + SQR (dy)); @@ -454,6 +482,9 @@ paint_core_interpolate (paint_core, drawable) paint_core->curx = paint_core->lastx + dx * t; paint_core->cury = paint_core->lasty + dy * t; + paint_core->curpressure = paint_core->lastpressure + dpressure * t; + paint_core->curxtilt = paint_core->lastxtilt + dxtilt * t; + paint_core->curytilt = paint_core->lastytilt + dytilt * t; (* paint_core->paint_func) (paint_core, drawable, MOTION_PAINT); } } @@ -461,6 +492,9 @@ paint_core_interpolate (paint_core, drawable) paint_core->distance = total; paint_core->curx = paint_core->lastx + dx; paint_core->cury = paint_core->lasty + dy; + paint_core->curpressure = paint_core->lastpressure + dpressure; + paint_core->curxtilt = paint_core->lastxtilt + dxtilt; + paint_core->curytilt = paint_core->lastytilt + dytilt; } void @@ -478,6 +512,7 @@ paint_core_finish (paint_core, drawable, tool_id) /* Determine if any part of the image has been altered-- * if nothing has, then just return... */ + if ((paint_core->x2 == paint_core->x1) || (paint_core->y2 == paint_core->y1)) return; @@ -487,6 +522,9 @@ paint_core_finish (paint_core, drawable, tool_id) pu->tool_ID = tool_id; pu->lastx = paint_core->startx; pu->lasty = paint_core->starty; + pu->lastpressure = paint_core->startpressure; + pu->lastxtilt = paint_core->startxtilt; + pu->lastytilt = paint_core->startytilt; /* Push a paint undo */ undo_push_paint (gimage, pu); @@ -742,6 +780,95 @@ paint_core_subsample_mask (mask, x, y) return dest; } +static MaskBuf * +paint_core_pressurize_mask (brush_mask, x, y, pressure) + MaskBuf * brush_mask; + double x, y; + double pressure; +{ + static MaskBuf *last_brush = NULL; + static double map[256]; + static unsigned char mapi[256]; + unsigned char *source; + unsigned char *dest; + MaskBuf *subsample_mask; + int i; + double ds,s,c; + + /* Get the raw subsampled mask */ + subsample_mask = paint_core_subsample_mask(brush_mask, x, y); + + /* Special case pressure = 0.5 */ + if ((int)(pressure*100+0.5) == 50) + return subsample_mask; + + /* Make sure we have the right sized buffer */ + if (brush_mask != last_brush) + { + if (pressure_brush) + mask_buf_free (pressure_brush); + pressure_brush = mask_buf_new (brush_mask->width + 2, + brush_mask->height +2); + } + + /* Create the pressure profile + + It is: I'(I) = tanh(20*(pressure-0.5)*I) : pressure > 0.5 + I'(I) = 1 - tanh(20*(0.5-pressure)*(1-I)) : pressure < 0.5 + + It looks like: + + low pressure medium pressure high pressure + + | / -- + | / / + / / | + -- / | + + */ + + ds = (pressure - 0.5)*(20./256.); + s = 0; + c = 1.0; + + if (ds > 0) + { + for (i=0;i<256;i++) + { + map[i] = s/c; + s += c*ds; + c += s*ds; + } + for (i=0;i<256;i++) + mapi[i] = (int)(255*map[i]/map[255]); + } + else + { + ds = -ds; + for (i=255;i>=0;i--) + { + map[i] = s/c; + s += c*ds; + c += s*ds; + } + for (i=0;i<256;i++) + mapi[i] = (int)(255*(1-map[i]/map[0])); + } + + /* Now convert the brush */ + + source = mask_buf_data (subsample_mask); + dest = mask_buf_data (pressure_brush); + + i = subsample_mask->width * subsample_mask->height; + while (i--) + { + *dest++ = mapi[(*source++)]; + } + + return pressure_brush; +} + static MaskBuf * paint_core_solidify_mask (brush_mask) MaskBuf * brush_mask; @@ -790,6 +917,9 @@ paint_core_get_brush_mask (paint_core, brush_hardness) case HARD: bm = paint_core_solidify_mask (paint_core->brush_mask); break; + case PRESSURE: + bm = paint_core_pressurize_mask (paint_core->brush_mask, paint_core->curx, paint_core->cury, paint_core->curpressure); + break; default: bm = NULL; break; diff --git a/app/tools/paint_core.h b/app/tools/paint_core.h index 0ed4938534..0d73a98416 100644 --- a/app/tools/paint_core.h +++ b/app/tools/paint_core.h @@ -31,6 +31,7 @@ /* brush application types */ #define HARD 0 /* pencil */ #define SOFT 1 /* paintbrush */ +#define PRESSURE 2 /* paintbrush with variable pressure */ /* paint application modes */ #define CONSTANT 0 /* pencil, paintbrush, airbrush, clone */ @@ -44,12 +45,21 @@ struct _paint_core double startx; /* starting x coord */ double starty; /* starting y coord */ + double startpressure; /* starting pressure */ + double startxtilt; /* starting xtilt */ + double startytilt; /* starting ytilt */ double curx; /* current x coord */ double cury; /* current y coord */ + double curpressure; /* current pressure */ + double curxtilt; /* current xtilt */ + double curytilt; /* current ytilt */ double lastx; /* last x coord */ double lasty; /* last y coord */ + double lastpressure; /* last pressure */ + double lastxtilt; /* last xtilt */ + double lastytilt; /* last ytilt */ int state; /* state of buttons and keys */ @@ -74,6 +84,9 @@ struct _paint_undo int tool_ID; double lastx; double lasty; + double lastpressure; + double lastxtilt; + double lastytilt; }; /* paint tool action functions */ diff --git a/app/tools/paintbrush.c b/app/tools/paintbrush.c index 6bdbec0207..42d0b9079a 100644 --- a/app/tools/paintbrush.c +++ b/app/tools/paintbrush.c @@ -224,7 +224,7 @@ paintbrush_motion (PaintCore *paint_core, /* paste the newly painted canvas to the gimage which is being worked on */ paint_core_paste_canvas (paint_core, drawable, blend, (int) (get_brush_opacity () * 255), - get_brush_paint_mode (), SOFT, + get_brush_paint_mode (), PRESSURE, incremental ? INCREMENTAL : CONSTANT); } } diff --git a/app/tools/tools.c b/app/tools/tools.c index 1528eab3ef..993df27c79 100644 --- a/app/tools/tools.c +++ b/app/tools/tools.c @@ -31,6 +31,7 @@ #include "crop.h" #include "cursorutil.h" #include "curves.h" +#include "devices.h" #include "eraser.h" #include "gdisplay.h" #include "hue_saturation.h" @@ -58,12 +59,12 @@ Tool * active_tool = NULL; Layer * active_tool_layer = NULL; +ToolType active_tool_type = -1; /* Local Data */ static GtkWidget *options_shell = NULL; static GtkWidget *options_vbox = NULL; -static ToolType active_tool_type = -1; static int global_tool_ID = 0; @@ -362,6 +363,10 @@ tools_select (ToolType type) gtk_container_enable_resize (GTK_CONTAINER (options_shell)); + /* Update the device-information dialog */ + + device_status_update (current_device); + /* Set the paused count variable to 0 */ active_tool->paused_count = 0; @@ -583,7 +588,6 @@ tools_options_dialog_new () gtk_widget_show (options_vbox); } - void tools_options_dialog_show () { diff --git a/app/tools/tools.h b/app/tools/tools.h index 7643a13804..c253d16d88 100644 --- a/app/tools/tools.h +++ b/app/tools/tools.h @@ -135,6 +135,7 @@ struct _ToolInfo /* Global Data Structure */ extern Tool * active_tool; +extern ToolType active_tool_type; extern Layer * active_tool_layer; extern ToolInfo tool_info[]; diff --git a/app/undo.c b/app/undo.c index d3da62bd50..234d194430 100644 --- a/app/undo.c +++ b/app/undo.c @@ -1018,6 +1018,18 @@ undo_pop_paint (GImage *gimage, pc->lasty = pu->lasty; pu->lasty = tmp; + tmp = pc->lastpressure; + pc->lastpressure = pu->lastpressure; + pu->lastpressure = tmp; + + tmp = pc->lastxtilt; + pc->lastxtilt = pu->lastxtilt; + pu->lastxtilt = tmp; + + tmp = pc->lastytilt; + pc->lastytilt = pu->lastytilt; + pu->lastytilt = tmp; + return TRUE; } diff --git a/app/widgets/gimpitemfactory.c b/app/widgets/gimpitemfactory.c index 25ff8baecf..86a0f91e0d 100644 --- a/app/widgets/gimpitemfactory.c +++ b/app/widgets/gimpitemfactory.c @@ -57,6 +57,8 @@ static GtkMenuEntry menu_items[] = { "/File/Dialogs/Palette...", "P", dialogs_palette_cmd_callback, NULL }, { "/File/Dialogs/Gradient Editor...", "G", dialogs_gradient_editor_cmd_callback, NULL }, { "/File/Dialogs/Tool Options...", "T", dialogs_tools_options_cmd_callback, NULL }, + { "/File/Dialogs/Input Devices...", NULL, dialogs_input_devices_cmd_callback, NULL }, + { "/File/Dialogs/Device Status...", NULL, dialogs_device_status_cmd_callback, NULL }, { "/File/",NULL,NULL,NULL}, @@ -199,6 +201,8 @@ static GtkMenuEntry menu_items[] = { "/Dialogs/Layers & Channels...", "L", dialogs_lc_cmd_callback, NULL }, { "/Dialogs/Indexed Palette...", NULL, dialogs_indexed_palette_cmd_callback, NULL }, { "/Dialogs/Tool Options...", NULL, dialogs_tools_options_cmd_callback, NULL }, + { "/Dialogs/Input Devices...", NULL, dialogs_input_devices_cmd_callback, NULL }, + { "/Dialogs/Device Status...", NULL, dialogs_device_status_cmd_callback, NULL }, { "/Automatic", NULL, file_load_by_extension_callback, NULL }, { "/", NULL, NULL, NULL }, diff --git a/app/widgets/gimppaletteeditor.c b/app/widgets/gimppaletteeditor.c index ee1f8afbe5..177a6cad19 100644 --- a/app/widgets/gimppaletteeditor.c +++ b/app/widgets/gimppaletteeditor.c @@ -29,6 +29,7 @@ #include "color_area.h" #include "color_select.h" #include "datafiles.h" +#include "devices.h" #include "errors.h" #include "general.h" #include "gimprc.h" @@ -338,6 +339,7 @@ palette_set_foreground (int r, { store_color (&foreground_pixel, rr, gg, bb); color_area_update (); + device_status_update (current_device); } }