gromit-20041213/0000755000175000017500000000000010157274045013552 5ustar simonsimon00000000000000gromit-20041213/COPYING0000644000175000017500000004311507342200356014605 0ustar simonsimon00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. gromit-20041213/erase_cursor_mask.xbm0000644000175000017500000000174607342200357017776 0ustar simonsimon00000000000000/* Created with The GIMP */ #define erase_cursor_mask_width 31 #define erase_cursor_mask_height 31 #define erase_cursor_mask_x_hot 14 #define erase_cursor_mask_y_hot 14 static unsigned char erase_cursor_mask_bits[] = { 0x00, 0xf8, 0x03, 0x00, 0x00, 0xff, 0x1f, 0x00, 0xc0, 0x07, 0x7c, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0x30, 0x00, 0x80, 0x01, 0x18, 0x00, 0x00, 0x03, 0x0c, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x0c, 0x06, 0x00, 0x00, 0x0c, 0x06, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, 0x18, 0x06, 0x00, 0x00, 0x0c, 0x06, 0x00, 0x00, 0x0c, 0x06, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x06, 0x18, 0x00, 0x00, 0x03, 0x30, 0x00, 0x80, 0x01, 0xe0, 0x00, 0xe0, 0x00, 0xc0, 0x07, 0x7c, 0x00, 0x00, 0xff, 0x1f, 0x00, 0x00, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; gromit-20041213/erase_cursor.xbm0000644000175000017500000000171507342200357016757 0ustar simonsimon00000000000000/* Created with The GIMP */ #define erase_cursor_width 31 #define erase_cursor_height 31 #define erase_cursor_x_hot 14 #define erase_cursor_y_hot 14 static unsigned char erase_cursor_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00, 0x00, 0x07, 0x1c, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x20, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x01, 0x20, 0x00, 0x80, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x00, 0x07, 0x1c, 0x00, 0x00, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; gromit-20041213/gromit.c0000644000175000017500000014017410157272744015232 0ustar simonsimon00000000000000/* Gromit -- a program for painting on the screen * Copyright (C) 2000 Simon Budig * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include "paint_cursor.xbm" #include "paint_cursor_mask.xbm" #include "erase_cursor.xbm" #include "erase_cursor_mask.xbm" #define GROMIT_MOUSE_EVENTS ( GDK_PROXIMITY_IN_MASK | \ GDK_PROXIMITY_OUT_MASK | \ GDK_BUTTON_MOTION_MASK | \ GDK_BUTTON_PRESS_MASK | \ GDK_BUTTON_RELEASE_MASK ) #define GROMIT_PAINT_AREA_EVENTS ( GROMIT_MOUSE_EVENTS | GDK_EXPOSURE_MASK ) #define GROMIT_WINDOW_EVENTS ( GROMIT_PAINT_AREA_EVENTS ) /* Atoms used to control Gromit */ #define GA_CONTROL gdk_atom_intern ("Gromit/control", FALSE) #define GA_STATUS gdk_atom_intern ("Gromit/status", FALSE) #define GA_QUIT gdk_atom_intern ("Gromit/quit", FALSE) #define GA_ACTIVATE gdk_atom_intern ("Gromit/activate", FALSE) #define GA_DEACTIVATE gdk_atom_intern ("Gromit/deactivate", FALSE) #define GA_TOGGLE gdk_atom_intern ("Gromit/toggle", FALSE) #define GA_VISIBILITY gdk_atom_intern ("Gromit/visibility", FALSE) #define GA_CLEAR gdk_atom_intern ("Gromit/clear", FALSE) typedef enum { GROMIT_PEN, GROMIT_ERASER, GROMIT_RECOLOR } GromitPaintType; typedef struct { GromitPaintType type; guint width; gfloat arrowsize; GdkColor *fg_color; GdkGC *paint_gc; GdkGC *shape_gc; gdouble pressure; } GromitPaintContext; typedef struct { gint x; gint y; gint width; } GromitStrokeCoordinate; typedef struct { GtkWidget *win; GtkWidget *area; GtkWidget *panel; GtkWidget *button; GdkCursor *paint_cursor; GdkCursor *erase_cursor; GdkPixmap *pixmap; GdkDisplay *display; GdkScreen *screen; GdkWindow *root; gchar *hot_keyval; guint hot_keycode; GdkColormap *cm; GdkColor *white; GdkColor *black; GdkColor *red; GromitPaintContext *default_pen; GromitPaintContext *default_eraser; GromitPaintContext *cur_context; GHashTable *tool_config; GdkBitmap *shape; GdkGC *shape_gc; GdkGCValues *shape_gcv; GdkColor *transparent; GdkColor *opaque; gdouble lastx; gdouble lasty; guint32 motion_time; GList *coordlist; GdkDevice *device; guint state; guint timeout_id; guint modified; guint delayed; guint maxwidth; guint width; guint height; guint hard_grab; guint client; guint painted; guint hidden; } GromitData; /* I need a prototype... */ void gromit_release_grab (GromitData *data); void gromit_acquire_grab (GromitData *data); GromitPaintContext * gromit_paint_context_new (GromitData *data, GromitPaintType type, GdkColor *fg_color, guint width, guint arrowsize) { GromitPaintContext *context; GdkGCValues shape_gcv; context = g_malloc (sizeof (GromitPaintContext)); context->type = type; context->width = width; context->arrowsize = arrowsize; context->fg_color = fg_color; if (type == GROMIT_ERASER) { context->paint_gc = NULL; } else { /* GROMIT_PEN || GROMIT_RECOLOR */ context->paint_gc = gdk_gc_new (data->pixmap); gdk_gc_set_foreground (context->paint_gc, fg_color); gdk_gc_set_line_attributes (context->paint_gc, width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); } if (type == GROMIT_RECOLOR) { context->shape_gc = NULL; } else { /* GROMIT_PEN || GROMIT_ERASER */ context->shape_gc = gdk_gc_new (data->shape); gdk_gc_get_values (context->shape_gc, &shape_gcv); if (type == GROMIT_ERASER) gdk_gc_set_foreground (context->shape_gc, &(shape_gcv.foreground)); else /* GROMIT_PEN */ gdk_gc_set_foreground (context->shape_gc, &(shape_gcv.background)); gdk_gc_set_line_attributes (context->shape_gc, width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); } return context; } void gromit_paint_context_print (gchar *name, GromitPaintContext *context) { g_printerr ("Tool name: \"%-20s\": ", name); switch (context->type) { case GROMIT_PEN: g_printerr ("Pen, "); break; case GROMIT_ERASER: g_printerr ("Eraser, "); break; case GROMIT_RECOLOR: g_printerr ("Recolor, "); break; default: g_printerr ("UNKNOWN, "); break; } g_printerr ("width: %3d, ", context->width); g_printerr ("arrowsize: %.2f, ", context->arrowsize); g_printerr ("color: #%02X%02X%02X\n", context->fg_color->red >> 8, context->fg_color->green >> 8, context->fg_color->blue >> 8); } void gromit_paint_context_free (GromitPaintContext *context) { g_object_unref (context->paint_gc); g_object_unref (context->shape_gc); g_free (context); } void gromit_coord_list_prepend (GromitData *data, gint x, gint y, gint width) { GromitStrokeCoordinate *point; point = g_malloc (sizeof (GromitStrokeCoordinate)); point->x = x; point->y = y; point->width = width; data->coordlist = g_list_prepend (data->coordlist, point); } void gromit_coord_list_free (GromitData *data) { GList *ptr; ptr = data->coordlist; while (ptr) { g_free (ptr->data); ptr = ptr->next; } g_list_free (data->coordlist); data->coordlist = NULL; } gboolean gromit_coord_list_get_arrow_param (GromitData *data, gint search_radius, gint *ret_width, gfloat *ret_direction) { gint x0, y0, r2, dist; gboolean success = FALSE; GromitStrokeCoordinate *cur_point, *valid_point; GList *ptr = data->coordlist; gfloat width; valid_point = NULL; if (ptr) { cur_point = ptr->data; x0 = cur_point->x; y0 = cur_point->y; r2 = search_radius * search_radius; dist = 0; while (ptr && dist < r2) { ptr = ptr->next; if (ptr) { cur_point = ptr->data; dist = (cur_point->x - x0) * (cur_point->x - x0) + (cur_point->y - y0) * (cur_point->y - y0); width = cur_point->width * data->cur_context->arrowsize; if (width * 2 <= dist && (!valid_point || valid_point->width < cur_point->width)) valid_point = cur_point; } } if (valid_point) { *ret_width = MAX (valid_point->width * data->cur_context->arrowsize, 2); *ret_direction = atan2 (y0 - valid_point->y, x0 - valid_point->x); success = TRUE; } } return success; } void gromit_hide_window (GromitData *data) { if (!data->hidden) { if (data->hard_grab) data->hidden = 2; else data->hidden = 1; gromit_release_grab (data); gtk_widget_hide (data->win); } } void gromit_show_window (GromitData *data) { gint oldstatus = data->hidden; if (data->hidden) { gtk_widget_show (data->win); data->hidden = 0; if (oldstatus == 2) gromit_acquire_grab (data); } gdk_window_raise (data->win->window); } void gromit_toggle_visibility (GromitData *data) { if (data->hidden) gromit_show_window (data); else gromit_hide_window (data); } void gromit_release_grab (GromitData *data) { if (data->hard_grab) { data->hard_grab = 0; gdk_display_pointer_ungrab (data->display, GDK_CURRENT_TIME); /* inherit cursor from root window */ gdk_window_set_cursor (data->win->window, NULL); } if (!data->painted) gromit_hide_window (data); } void gromit_acquire_grab (GromitData *data) { GdkGrabStatus result; gromit_show_window (data); if (!data->hard_grab) { result = gdk_pointer_grab (data->area->window, FALSE, GROMIT_MOUSE_EVENTS, 0, NULL /* data->paint_cursor */, GDK_CURRENT_TIME); switch (result) { case GDK_GRAB_SUCCESS: data->hard_grab = 1; break; case GDK_GRAB_ALREADY_GRABBED: g_printerr ("Grabbing Pointer failed: %s\n", "AlreadyGrabbed"); break; case GDK_GRAB_INVALID_TIME: g_printerr ("Grabbing Pointer failed: %s\n", "GrabInvalidTime"); break; case GDK_GRAB_NOT_VIEWABLE: g_printerr ("Grabbing Pointer failed: %s\n", "GrabNotViewable"); break; case GDK_GRAB_FROZEN: g_printerr ("Grabbing Pointer failed: %s\n", "GrabFrozen"); break; default: g_printerr ("Grabbing Pointer failed: %s\n", "Unknown error"); } if (data->cur_context->type == GROMIT_ERASER) gdk_window_set_cursor (data->win->window, data->erase_cursor); else gdk_window_set_cursor (data->win->window, data->paint_cursor); } } void gromit_toggle_grab (GromitData *data) { if (data->hard_grab) gromit_release_grab (data); else gromit_acquire_grab (data); } void gromit_clear_screen (GromitData *data) { gdk_gc_set_foreground (data->shape_gc, data->transparent); gdk_draw_rectangle (data->shape, data->shape_gc, 1, 0, 0, data->width, data->height); gtk_widget_shape_combine_mask (data->win, data->shape, 0,0); if (!data->hard_grab) gromit_hide_window (data); data->painted = 0; } gint reshape (gpointer user_data) { GromitData *data = (GromitData *) user_data; if (data->modified) { if (gtk_events_pending () && data->delayed < 5) { data->delayed++ ; } else { gtk_widget_shape_combine_mask (data->win, data->shape, 0,0); data->modified = 0; data->delayed = 0; } } return 1; } void gromit_select_tool (GromitData *data, GdkDevice *device, guint state) { guint buttons = 0, modifier = 0, len = 0; guint req_buttons = 0, req_modifier = 0; guint i, j, success = 0; GromitPaintContext *context = NULL; guchar *name; if (device) { len = strlen (device->name); name = g_strndup (device->name, len + 3); /* Extract Button/Modifiers from state (see GdkModifierType) */ req_buttons = (state >> 8) & 31; req_modifier = (state >> 1) & 7; if (state & GDK_SHIFT_MASK) req_modifier |= 1; name [len] = 124; name [len+3] = 0; /* 0, 1, 3, 7, 15, 31 */ context = NULL; i=-1; do { i++; buttons = req_buttons & ((1 << i)-1); j=-1; do { j++; modifier = req_modifier & ((1 << j)-1); name [len+1] = buttons + 64; name [len+2] = modifier + 48; context = g_hash_table_lookup (data->tool_config, name); if (context) { data->cur_context = context; success = 1; } } while (j<=3 && req_modifier >= (1 << j)); } while (i<=5 && req_buttons >= (1 << i)); g_free (name); if (!success) { if (device->source == GDK_SOURCE_ERASER) data->cur_context = data->default_eraser; else data->cur_context = data->default_pen; } } else { g_printerr ("ERROR: Attempt to select nonexistent device!\n"); data->cur_context = data->default_pen; } if (data->cur_context->type == GROMIT_ERASER) gdk_window_set_cursor (data->win->window, data->erase_cursor); else gdk_window_set_cursor (data->win->window, data->paint_cursor); data->state = state; data->device = device; } void gromit_draw_line (GromitData *data, gint x1, gint y1, gint x2, gint y2) { GdkRectangle rect; rect.x = MIN (x1,x2) - data->maxwidth / 2; rect.y = MIN (y1,y2) - data->maxwidth / 2; rect.width = ABS (x1-x2) + data->maxwidth; rect.height = ABS (y1-y2) + data->maxwidth; if (data->cur_context->paint_gc) gdk_gc_set_line_attributes (data->cur_context->paint_gc, data->maxwidth, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); if (data->cur_context->shape_gc) gdk_gc_set_line_attributes (data->cur_context->shape_gc, data->maxwidth, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); if (data->cur_context->paint_gc) gdk_draw_line (data->pixmap, data->cur_context->paint_gc, x1, y1, x2, y2); if (data->cur_context->shape_gc) { gdk_draw_line (data->shape, data->cur_context->shape_gc, x1, y1, x2, y2); data->modified = 1; } if (data->cur_context->paint_gc) gtk_widget_draw (data->area, &rect); data->painted = 1; } void gromit_draw_arrow (GromitData *data, gint x1, gint y1, gint width, gfloat direction) { GdkRectangle rect; GdkPoint arrowhead [4]; width = width / 2; /* I doubt that calculating the boundary box more exact is very useful */ rect.x = x1 - 4 * width - 1; rect.y = y1 - 4 * width - 1; rect.width = 8 * width + 2; rect.height = 8 * width + 2; arrowhead [0].x = x1 + 4 * width * cos (direction); arrowhead [0].y = y1 + 4 * width * sin (direction); arrowhead [1].x = x1 - 3 * width * cos (direction) + 3 * width * sin (direction); arrowhead [1].y = y1 - 3 * width * cos (direction) - 3 * width * sin (direction); arrowhead [2].x = x1 - 2 * width * cos (direction); arrowhead [2].y = y1 - 2 * width * sin (direction); arrowhead [3].x = x1 - 3 * width * cos (direction) - 3 * width * sin (direction); arrowhead [3].y = y1 + 3 * width * cos (direction) - 3 * width * sin (direction); if (data->cur_context->paint_gc) gdk_gc_set_line_attributes (data->cur_context->paint_gc, 0, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); if (data->cur_context->shape_gc) gdk_gc_set_line_attributes (data->cur_context->shape_gc, 0, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); if (data->cur_context->paint_gc) { gdk_draw_polygon (data->pixmap, data->cur_context->paint_gc, TRUE, arrowhead, 4); gdk_gc_set_foreground (data->cur_context->paint_gc, data->black); gdk_draw_polygon (data->pixmap, data->cur_context->paint_gc, FALSE, arrowhead, 4); gdk_gc_set_foreground (data->cur_context->paint_gc, data->cur_context->fg_color); } if (data->cur_context->shape_gc) { gdk_draw_polygon (data->shape, data->cur_context->shape_gc, TRUE, arrowhead, 4); gdk_draw_polygon (data->shape, data->cur_context->shape_gc, FALSE, arrowhead, 4); data->modified = 1; } if (data->cur_context->paint_gc) gtk_widget_draw (data->area, &rect); data->painted = 1; } /* * Event-Handlers to perform the drawing */ gboolean proximity_in (GtkWidget *win, GdkEventProximity *ev, gpointer user_data) { GromitData *data = (GromitData *) user_data; gint x, y; GdkModifierType state; gdk_window_get_pointer (data->win->window, &x, &y, &state); gromit_select_tool (data, ev->device, state); return TRUE; } gboolean proximity_out (GtkWidget *win, GdkEventProximity *ev, gpointer user_data) { GromitData *data = (GromitData *) user_data; data->cur_context = data->default_pen; if (data->cur_context->type == GROMIT_ERASER) gdk_window_set_cursor (data->win->window, data->erase_cursor); else gdk_window_set_cursor (data->win->window, data->paint_cursor); data->state = 0; data->device = NULL; return FALSE; } gboolean paint (GtkWidget *win, GdkEventButton *ev, gpointer user_data) { GromitData *data = (GromitData *) user_data; gdouble pressure = 0.5; if (!data->hard_grab) return FALSE; /* See GdkModifierType. Am I fixing a Gtk misbehaviour??? */ ev->state |= 1 << (ev->button + 7); if (ev->state != data->state || ev->device != data->device) gromit_select_tool (data, ev->device, ev->state); gdk_window_set_background (data->area->window, data->cur_context->fg_color); data->lastx = ev->x; data->lasty = ev->y; data->motion_time = ev->time; if (ev->device->source == GDK_SOURCE_MOUSE) { data->maxwidth = data->cur_context->width; } else { gdk_event_get_axis ((GdkEvent *) ev, GDK_AXIS_PRESSURE, &pressure); data->maxwidth = (CLAMP (pressure * pressure,0,1) * (double) data->cur_context->width); } if (ev->button <= 5) gromit_draw_line (data, ev->x, ev->y, ev->x, ev->y); gromit_coord_list_prepend (data, ev->x, ev->y, data->maxwidth); /* if (data->cur_context->shape_gc && !gtk_events_pending ()) gtk_widget_shape_combine_mask (data->win, data->shape, 0,0); */ return TRUE; } gboolean paintto (GtkWidget *win, GdkEventMotion *ev, gpointer user_data) { GromitData *data = (GromitData *) user_data; GdkTimeCoord **coords = NULL; int nevents; int i; gboolean ret; gdouble pressure = 0.5; if (!data->hard_grab) return FALSE; if (ev->state != data->state || ev->device != data->device) gromit_select_tool (data, ev->device, ev->state); ret = gdk_device_get_history (ev->device, ev->window, data->motion_time, ev->time, &coords, &nevents); /* g_printerr ("Got %d coords\n", nevents); */ if (coords) { for (i=0; i < nevents; i++) { gdouble x, y; gdk_device_get_axis (ev->device, coords[i]->axes, GDK_AXIS_PRESSURE, &pressure); if (pressure > 0) { if (ev->device->source == GDK_SOURCE_MOUSE) data->maxwidth = data->cur_context->width; else data->maxwidth = (CLAMP (pressure * pressure, 0, 1) * (double) data->cur_context->width); gdk_device_get_axis(ev->device, coords[i]->axes, GDK_AXIS_X, &x); gdk_device_get_axis(ev->device, coords[i]->axes, GDK_AXIS_Y, &y); gromit_draw_line (data, data->lastx, data->lasty, x, y); gromit_coord_list_prepend (data, x, y, data->maxwidth); data->lastx = x; data->lasty = y; } } data->motion_time = coords[nevents-1]->time; g_free (coords); } /* always paint to the current event coordinate. */ gdk_event_get_axis ((GdkEvent *) ev, GDK_AXIS_PRESSURE, &pressure); if (pressure > 0) { if (ev->device->source == GDK_SOURCE_MOUSE) data->maxwidth = data->cur_context->width; else data->maxwidth = (CLAMP (pressure * pressure,0,1) * (double) data->cur_context->width); gromit_draw_line (data, data->lastx, data->lasty, ev->x, ev->y); gromit_coord_list_prepend (data, ev->x, ev->y, data->maxwidth); } data->lastx = ev->x; data->lasty = ev->y; return TRUE; } gboolean paintend (GtkWidget *win, GdkEventButton *ev, gpointer user_data) { GromitData *data = (GromitData *) user_data; gint width = data->cur_context->arrowsize * data->cur_context->width / 2; gfloat direction = 0; if ((ev->x != data->lastx) || (ev->y != data->lasty)) paintto (win, (GdkEventMotion *) ev, user_data); if (!data->hard_grab) return FALSE; if (data->cur_context->arrowsize != 0 && gromit_coord_list_get_arrow_param (data, width * 3, &width, &direction)) gromit_draw_arrow (data, ev->x, ev->y, width, direction); gromit_coord_list_free (data); return TRUE; } /* * Functions for handling various (GTK+)-Events */ void quiet_print_handler (const gchar *string) { return; } gboolean event_configure (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { GromitData *data = (GromitData *) user_data; data->pixmap = gdk_pixmap_new (data->area->window, data->width, data->height, -1); gdk_draw_rectangle (data->pixmap, data->area->style->black_gc, 1, 0, 0, data->width, data->height); gdk_window_set_transient_for (data->area->window, data->win->window); return TRUE; } gboolean event_expose (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { GromitData *data = (GromitData *) user_data; gdk_draw_drawable (data->area->window, data->area->style->fg_gc[GTK_WIDGET_STATE (data->area)], data->pixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); return TRUE; } /* Keyboard control */ gint key_press_event (GtkWidget *grab_widget, GdkEventKey *event, gpointer func_data) { GromitData *data = (GromitData *) func_data; if (event->type == GDK_KEY_PRESS && event->hardware_keycode == data->hot_keycode) { if (event->state & GDK_SHIFT_MASK) gromit_clear_screen (data); else if (event->state & GDK_CONTROL_MASK) gromit_toggle_visibility (data); else if (event->state & GDK_MOD1_MASK) gtk_main_quit (); else gromit_toggle_grab (data); return TRUE; } return FALSE; } void gromit_main_do_event (GdkEventAny *event, GromitData *data) { if ((event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE) && event->window == data->root && ((GdkEventKey *) event)->hardware_keycode == data->hot_keycode) { /* redirect the event to our main window, so that GTK+ doesn't * throw it away (there is no GtkWidget for the root window...) */ event->window = data->win->window; g_object_ref (data->win->window); } gtk_main_do_event ((GdkEvent *) event); } /* Remote control */ void event_selection_get (GtkWidget *widget, GtkSelectionData *selection_data, guint info, guint time, gpointer data) { gchar *uri = "OK"; if (selection_data->target == GA_TOGGLE) gromit_toggle_grab (data); else if (selection_data->target == GA_VISIBILITY) gromit_toggle_visibility (data); else if (selection_data->target == GA_CLEAR) gromit_clear_screen (data); else if (selection_data->target == GA_QUIT) gtk_main_quit (); else uri = "NOK"; gtk_selection_data_set (selection_data, selection_data->target, 8, uri, strlen (uri)); } void event_selection_received (GtkWidget *widget, GtkSelectionData *selection_data, guint time, gpointer user_data) { GromitData *data = (GromitData *) user_data; /* If someone has a selection for us, Gromit is already running. */ if (selection_data->type == GDK_NONE) data->client = 0; else data->client = 1; gtk_main_quit (); } /* * Functions for parsing the Configuration-file */ gchar * parse_name (GScanner *scanner) { GTokenType token; guint buttons = 0; guint modifier = 0; guint len = 0; gchar *name; token = g_scanner_cur_token(scanner); if (token != G_TOKEN_STRING) { g_scanner_unexp_token (scanner, G_TOKEN_STRING, NULL, NULL, NULL, "aborting", TRUE); exit (1); } len = strlen (scanner->value.v_string); name = g_strndup (scanner->value.v_string, len + 3); token = g_scanner_get_next_token (scanner); /* * Are there any options to limit the scope of the definition? */ if (token == G_TOKEN_LEFT_BRACE) { g_scanner_set_scope (scanner, 1); scanner->config->int_2_float = 0; modifier = buttons = 0; while ((token = g_scanner_get_next_token (scanner)) != G_TOKEN_RIGHT_BRACE) { if (token == G_TOKEN_SYMBOL) { if ((guint) scanner->value.v_symbol < 11) buttons |= 1 << ((guint) scanner->value.v_symbol - 1); else modifier |= 1 << ((guint) scanner->value.v_symbol - 11); } else if (token == G_TOKEN_INT) { if (scanner->value.v_int <= 5 && scanner->value.v_int > 0) buttons |= 1 << (scanner->value.v_int - 1); else g_printerr ("Only Buttons 1-5 are supported!\n"); } else { g_printerr ("skipped token\n"); } } g_scanner_set_scope (scanner, 0); scanner->config->int_2_float = 1; token = g_scanner_get_next_token (scanner); } name [len] = 124; name [len+1] = buttons + 64; name [len+2] = modifier + 48; name [len+3] = 0; return name; } void parse_config (GromitData *data) { GromitPaintContext *context=NULL; GromitPaintContext *context_template=NULL; GScanner *scanner; GTokenType token; gchar *filename; int file; gchar *name, *copy; GromitPaintType type; GdkColor *fg_color=NULL; guint width, arrowsize; filename = g_strjoin (G_DIR_SEPARATOR_S, g_get_home_dir(), ".gromitrc", NULL); file = open (filename, O_RDONLY); if (file < 0) { g_printerr ("Could not open %s: %s\n", filename, g_strerror (errno)); g_free (filename); return; } scanner = g_scanner_new (NULL); scanner->input_name = filename; scanner->config->case_sensitive = 0; scanner->config->scan_octal = 0; scanner->config->identifier_2_string = 0; scanner->config->char_2_token = 1; scanner->config->numbers_2_int = 1; scanner->config->int_2_float = 1; g_scanner_scope_add_symbol (scanner, 0, "PEN", (gpointer) GROMIT_PEN); g_scanner_scope_add_symbol (scanner, 0, "ERASER", (gpointer) GROMIT_ERASER); g_scanner_scope_add_symbol (scanner, 0, "RECOLOR",(gpointer) GROMIT_RECOLOR); g_scanner_scope_add_symbol (scanner, 1, "BUTTON1", (gpointer) 1); g_scanner_scope_add_symbol (scanner, 1, "BUTTON2", (gpointer) 2); g_scanner_scope_add_symbol (scanner, 1, "BUTTON3", (gpointer) 3); g_scanner_scope_add_symbol (scanner, 1, "BUTTON4", (gpointer) 4); g_scanner_scope_add_symbol (scanner, 1, "BUTTON5", (gpointer) 5); g_scanner_scope_add_symbol (scanner, 1, "SHIFT", (gpointer) 11); g_scanner_scope_add_symbol (scanner, 1, "CONTROL", (gpointer) 12); g_scanner_scope_add_symbol (scanner, 1, "META", (gpointer) 13); g_scanner_scope_add_symbol (scanner, 1, "ALT", (gpointer) 13); g_scanner_scope_add_symbol (scanner, 2, "size", (gpointer) 1); g_scanner_scope_add_symbol (scanner, 2, "color", (gpointer) 2); g_scanner_scope_add_symbol (scanner, 2, "arrowsize", (gpointer) 3); g_scanner_set_scope (scanner, 0); scanner->config->scope_0_fallback = 0; g_scanner_input_file (scanner, file); token = g_scanner_get_next_token (scanner); while (token != G_TOKEN_EOF) { /* * New tool definition */ if (token == G_TOKEN_STRING) { name = parse_name (scanner); token = g_scanner_cur_token(scanner); if (token != G_TOKEN_EQUAL_SIGN) { g_scanner_unexp_token (scanner, G_TOKEN_EQUAL_SIGN, NULL, NULL, NULL, "aborting", TRUE); exit (1); } token = g_scanner_get_next_token (scanner); /* defaults */ type = GROMIT_PEN; width = 7; arrowsize = 0; fg_color = data->red; if (token == G_TOKEN_SYMBOL) { type = (GromitPaintType) scanner->value.v_symbol; token = g_scanner_get_next_token (scanner); } else if (token == G_TOKEN_STRING) { copy = parse_name (scanner); token = g_scanner_cur_token(scanner); context_template = g_hash_table_lookup (data->tool_config, copy); if (context_template) { type = context_template->type; width = context_template->width; arrowsize = context_template->arrowsize; fg_color = context_template->fg_color; } else { g_printerr ("WARNING: Unable to copy \"%s\": " "not yet defined!\n", copy); } } else { g_printerr ("Expected Tool-definition " "or name of template tool\n"); exit (1); } /* Are there any tool-options? */ if (token == G_TOKEN_LEFT_PAREN) { GdkColor *color = NULL; g_scanner_set_scope (scanner, 2); scanner->config->int_2_float = 1; token = g_scanner_get_next_token (scanner); while (token != G_TOKEN_RIGHT_PAREN) { if (token == G_TOKEN_SYMBOL) { if ((guint) scanner->value.v_symbol == 1) { token = g_scanner_get_next_token (scanner); if (token != G_TOKEN_EQUAL_SIGN) { g_printerr ("Missing \"=\"... aborting\n"); exit (1); } token = g_scanner_get_next_token (scanner); if (token != G_TOKEN_FLOAT) { g_printerr ("Missing Size (float)... aborting\n"); exit (1); } width = (guint) (scanner->value.v_float + 0.5); } else if ((guint) scanner->value.v_symbol == 2) { token = g_scanner_get_next_token (scanner); if (token != G_TOKEN_EQUAL_SIGN) { g_printerr ("Missing \"=\"... aborting\n"); exit (1); } token = g_scanner_get_next_token (scanner); if (token != G_TOKEN_STRING) { g_printerr ("Missing Color (string)... " "aborting\n"); exit (1); } color = g_malloc (sizeof (GdkColor)); if (gdk_color_parse (scanner->value.v_string, color)) { if (gdk_colormap_alloc_color (data->cm, color, 0, 1)) { fg_color = color; } else { g_printerr ("Unable to allocate color. " "Keeping default!\n"); g_free (color); } } else { g_printerr ("Unable to parse color. " "Keeping default.\n"); g_free (color); } color = NULL; } else if ((guint) scanner->value.v_symbol == 3) { token = g_scanner_get_next_token (scanner); if (token != G_TOKEN_EQUAL_SIGN) { g_printerr ("Missing \"=\"... aborting\n"); exit (1); } token = g_scanner_get_next_token (scanner); if (token != G_TOKEN_FLOAT) { g_printerr ("Missing Arrowsize (float)... " "aborting\n"); exit (1); } arrowsize = scanner->value.v_float; } else { g_printerr ("Unknown tool type?????\n"); } } else { g_printerr ("skipped token!!!\n"); } token = g_scanner_get_next_token (scanner); } g_scanner_set_scope (scanner, 0); token = g_scanner_get_next_token (scanner); } /* * Finally we expect a semicolon */ if (token != ';') { g_printerr ("Expected \";\"\n"); exit (1); } context = gromit_paint_context_new (data, type, fg_color, width, arrowsize); g_hash_table_insert (data->tool_config, name, context); } else { g_printerr ("Expected name of Tool to define\n"); exit(1); } token = g_scanner_get_next_token (scanner); } g_scanner_destroy (scanner); close (file); g_free (filename); } /* * Functions for setting up (parts of) the application */ void setup_input_devices (GromitData *data) { GList *tmp_list; GdkDevice *core_pointer; core_pointer = gdk_display_get_core_pointer (data->display); for (tmp_list = gdk_display_list_devices (data->display); tmp_list; tmp_list = tmp_list->next) { GdkDevice *device = (GdkDevice *) tmp_list->data; /* Guess "Eraser"-Type devices */ if (strstr (device->name, "raser") || strstr (device->name, "RASER")) gdk_device_set_source (device, GDK_SOURCE_ERASER); /* Dont touch devices with two or less axis - GDK apparently * gets confused... */ if (device->num_axes > 2) { g_printerr ("Enabling No. %p: \"%s\" (Type: %d)\n", device, device->name, device->source); gdk_device_set_mode (device, GDK_MODE_SCREEN); } } } void setup_client_app (GromitData *data) { data->display = gdk_display_get_default (); data->screen = gdk_display_get_default_screen (data->display); data->root = gdk_screen_get_root_window (data->screen); data->width = gdk_screen_get_width (data->screen); data->height = gdk_screen_get_height (data->screen); data->hard_grab = 0; data->win = gtk_window_new (GTK_WINDOW_POPUP); gtk_widget_set_usize (GTK_WIDGET (data->win), data->width, data->height); gtk_widget_set_uposition (GTK_WIDGET (data->win), 0, 0); gtk_widget_set_events (data->win, GROMIT_WINDOW_EVENTS); gtk_signal_connect (GTK_OBJECT (data->win), "delete-event", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); gtk_signal_connect (GTK_OBJECT (data->win), "destroy", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); gtk_signal_connect (GTK_OBJECT (data->win), "selection_get", GTK_SIGNAL_FUNC (event_selection_get), (gpointer) data); gtk_signal_connect (GTK_OBJECT (data->win), "selection_received", GTK_SIGNAL_FUNC (event_selection_received), (gpointer) data); } void setup_main_app (GromitData *data, gboolean activate) { GdkPixmap *cursor_src, *cursor_mask; gboolean have_key = FALSE; /* COLORMAP */ data->cm = gdk_screen_get_default_colormap (data->screen); data->white = g_malloc (sizeof (GdkColor)); data->black = g_malloc (sizeof (GdkColor)); data->red = g_malloc (sizeof (GdkColor)); gdk_color_parse ("#FFFFFF", data->white); gdk_colormap_alloc_color (data->cm, data->white, FALSE, TRUE); gdk_color_parse ("#000000", data->black); gdk_colormap_alloc_color (data->cm, data->black, FALSE, TRUE); gdk_color_parse ("#FF0000", data->red); gdk_colormap_alloc_color (data->cm, data->red, FALSE, TRUE); /* CURSORS */ cursor_src = gdk_bitmap_create_from_data (NULL, paint_cursor_bits, paint_cursor_width, paint_cursor_height); cursor_mask = gdk_bitmap_create_from_data (NULL, paint_cursor_mask_bits, paint_cursor_width, paint_cursor_height); data->paint_cursor = gdk_cursor_new_from_pixmap (cursor_src, cursor_mask, data->white, data->black, paint_cursor_x_hot, paint_cursor_y_hot); g_object_unref (cursor_src); g_object_unref (cursor_mask); cursor_src = gdk_bitmap_create_from_data (NULL, erase_cursor_bits, erase_cursor_width, erase_cursor_height); cursor_mask = gdk_bitmap_create_from_data (NULL, erase_cursor_mask_bits, erase_cursor_width, erase_cursor_height); data->erase_cursor = gdk_cursor_new_from_pixmap (cursor_src, cursor_mask, data->white, data->black, erase_cursor_x_hot, erase_cursor_y_hot); g_object_unref (cursor_src); g_object_unref (cursor_mask); gdk_window_set_cursor (data->win->window, data->paint_cursor); /* SHAPE PIXMAP */ data->shape = gdk_pixmap_new (NULL, data->width, data->height, 1); data->shape_gc = gdk_gc_new (data->shape); data->shape_gcv = g_malloc (sizeof (GdkGCValues)); gdk_gc_get_values (data->shape_gc, data->shape_gcv); data->transparent = gdk_color_copy (&(data->shape_gcv->foreground)); data->opaque = gdk_color_copy (&(data->shape_gcv->background)); gdk_gc_set_foreground (data->shape_gc, data->transparent); gdk_draw_rectangle (data->shape, data->shape_gc, 1, 0, 0, data->width, data->height); /* DRAWING AREA */ data->area = gtk_drawing_area_new (); gtk_drawing_area_size (GTK_DRAWING_AREA (data->area), data->width, data->height); gtk_widget_set_events (data->area, GROMIT_PAINT_AREA_EVENTS); gtk_signal_connect (GTK_OBJECT (data->area), "expose_event", (GtkSignalFunc) event_expose, (gpointer) data); gtk_signal_connect (GTK_OBJECT (data->area),"configure_event", (GtkSignalFunc) event_configure, (gpointer) data); gtk_signal_connect (GTK_OBJECT (data->win), "motion_notify_event", GTK_SIGNAL_FUNC (paintto), (gpointer) data); gtk_signal_connect (GTK_OBJECT (data->win), "button_press_event", GTK_SIGNAL_FUNC (paint), (gpointer) data); gtk_signal_connect (GTK_OBJECT (data->win), "button_release_event", GTK_SIGNAL_FUNC (paintend), (gpointer) data); gtk_signal_connect (GTK_OBJECT (data->win), "proximity_in_event", GTK_SIGNAL_FUNC (proximity_in), (gpointer) data); gtk_signal_connect (GTK_OBJECT (data->win), "proximity_out_event", GTK_SIGNAL_FUNC (proximity_out), (gpointer) data); gtk_widget_set_extension_events (data->area, GDK_EXTENSION_EVENTS_ALL); gtk_container_add (GTK_CONTAINER (data->win), data->area); gtk_widget_shape_combine_mask (data->win, data->shape, 0,0); gtk_widget_show_all (data->area); gtk_widget_realize (data->win); data->painted = 0; gromit_hide_window (data); data->timeout_id = gtk_timeout_add (20, reshape, data); data->coordlist = NULL; data->modified = 0; data->default_pen = gromit_paint_context_new (data, GROMIT_PEN, data->red, 7, 0); data->default_eraser = gromit_paint_context_new (data, GROMIT_ERASER, data->red, 75, 0); data->cur_context = data->default_pen; /* * Parse Config file */ data->tool_config = g_hash_table_new (g_str_hash, g_str_equal); parse_config (data); data->state = 0; gtk_selection_owner_set (data->win, GA_CONTROL, GDK_CURRENT_TIME); gtk_selection_add_target (data->win, GA_CONTROL, GA_STATUS, 0); gtk_selection_add_target (data->win, GA_CONTROL, GA_QUIT, 1); gtk_selection_add_target (data->win, GA_CONTROL, GA_ACTIVATE, 2); gtk_selection_add_target (data->win, GA_CONTROL, GA_DEACTIVATE, 3); gtk_selection_add_target (data->win, GA_CONTROL, GA_TOGGLE, 4); gtk_selection_add_target (data->win, GA_CONTROL, GA_VISIBILITY, 5); gtk_selection_add_target (data->win, GA_CONTROL, GA_CLEAR, 6); setup_input_devices (data); /* Grab the GROMIT_HOTKEY */ if (data->hot_keyval) { GdkKeymap *keymap; GdkKeymapKey *keys; gint n_keys; guint keyval; if (strlen (data->hot_keyval) > 0 && strcasecmp (data->hot_keyval, "none") != 0) { keymap = gdk_keymap_get_for_display (data->display); keyval = gdk_keyval_from_name (data->hot_keyval); if (!keyval || !gdk_keymap_get_entries_for_keyval (keymap, keyval, &keys, &n_keys)) { g_printerr ("cannot find the key \"%s\"\n", data->hot_keyval); exit (1); } have_key = TRUE; data->hot_keycode = keys[0].keycode; g_free (keys); } } if (have_key) { if (data->hot_keycode) { gdk_error_trap_push (); XGrabKey (GDK_DISPLAY_XDISPLAY (data->display), data->hot_keycode, AnyModifier, GDK_WINDOW_XWINDOW (data->root), TRUE, GrabModeAsync, GrabModeAsync); gdk_flush (); if (gdk_error_trap_pop ()) { g_printerr ("could not grab Hotkey. Aborting...\n"); exit (1); } } else { g_printerr ("cannot find the key #%d\n", data->hot_keycode); exit (1); } } gdk_event_handler_set ((GdkEventFunc) gromit_main_do_event, data, NULL); gtk_key_snooper_install (key_press_event, data); if (activate) gromit_acquire_grab (data); } void parse_print_help (gpointer key, gpointer value, gpointer user_data) { gromit_paint_context_print ((gchar *) key, (GromitPaintContext *) value); } int app_parse_args (int argc, char **argv, GromitData *data) { gint i; gchar *arg; gboolean wrong_arg = FALSE; gboolean activate = FALSE; gboolean dump = FALSE; data->hot_keyval = "Pause"; data->hot_keycode = 0; for (i=1; i < argc ; i++) { arg = argv[i]; if (strcmp (arg, "-a") == 0 || strcmp (arg, "--active") == 0) { activate = TRUE; } else if (strcmp (arg, "-d") == 0 || strcmp (arg, "--debug") == 0) { dump = TRUE; } else if (strcmp (arg, "-k") == 0 || strcmp (arg, "--key") == 0) { if (i+1 < argc) { data->hot_keyval = argv[i+1]; data->hot_keycode = 0; i++; } else { g_printerr ("-k requires an Key-Name as argument\n"); wrong_arg = TRUE; } } else if (strcmp (arg, "-K") == 0 || strcmp (arg, "--keycode") == 0) { if (i+1 < argc && atoi (argv[i+1]) > 0) { data->hot_keyval = NULL; data->hot_keycode = atoi (argv[i+1]); i++; } else { g_printerr ("-K requires an keycode > 0 as argument\n"); wrong_arg = TRUE; } } else { g_printerr ("Unknown Option: \"%s\"\n", arg); wrong_arg = TRUE; } if (!wrong_arg) { if (dump) { g_printerr ("\n-----------------------------\n"); g_hash_table_foreach (data->tool_config, parse_print_help, NULL); g_printerr ("-----------------------------\n\n"); } } else { g_printerr ("Please see the Gromit README for the correct usage\n"); exit (1); } } return activate; } /* * Main programs */ int main_client (int argc, char **argv, GromitData *data) { GdkAtom action = GDK_NONE; gint i; gchar *arg; gboolean wrong_arg = FALSE; for (i=1; i < argc ; i++) { arg = argv[i]; action = GDK_NONE; if (strcmp (arg, "-t") == 0 || strcmp (arg, "--toggle") == 0) { action = GA_TOGGLE; } else if (strcmp (arg, "-v") == 0 || strcmp (arg, "--visibility") == 0) { action = GA_VISIBILITY; } else if (strcmp (arg, "-q") == 0 || strcmp (arg, "--quit") == 0) { action = GA_QUIT; } else if (strcmp (arg, "-c") == 0 || strcmp (arg, "--clear") == 0) { action = GA_CLEAR; } else { g_printerr ("Unknown Option: \"%s\"\n", arg); wrong_arg = TRUE; } if (!wrong_arg && action != GDK_NONE) { gtk_selection_convert (data->win, GA_CONTROL, action, GDK_CURRENT_TIME); gtk_main (); /* Wait for the response */ } else { g_printerr ("Please see the Gromit README for the correct usage\n"); return 1; } } return 0; } int main (int argc, char **argv) { GromitData *data; gtk_init (&argc, &argv); data = g_malloc (sizeof (GromitData)); /* g_set_printerr_handler (quiet_print_handler); */ setup_client_app (data); /* Try to get a status message. If there is a response gromit * is already acive. */ gtk_selection_convert (data->win, GA_CONTROL, GA_STATUS, GDK_CURRENT_TIME); gtk_main (); /* Wait for the response */ if (data->client) return main_client (argc, argv, data); /* Main application */ setup_main_app (data, app_parse_args (argc, argv, data)); gtk_main (); gdk_display_pointer_ungrab (data->display, GDK_CURRENT_TIME); gdk_cursor_unref (data->paint_cursor); gdk_cursor_unref (data->erase_cursor); g_free (data); return 0; } /* vim: sw=3 ts=8 cindent noai bs=2 cinoptions=(0 */ gromit-20041213/gromitrc_gsr0000644000175000017500000000451407405524662016207 0ustar simonsimon00000000000000/* GRaphics Over MIscellaneous Things RC file */ /* byGSR 2001-12-11 */ /* Pens */ "red Pen" = PEN (size = 7 color = "red"); "green Pen" = PEN (size = 7 color = "green"); "blue Pen" = PEN (size = 7 color = "blue"); # "yellow Pen" = PEN (size = 7 color = "yellow"); /* Quill */ "black Quill" = PEN (size = 4 color = "black"); "gray Quill" = PEN (size = 4 color = "gray50"); "white Quill" = PEN (size = 4 color = "white"); /* Smart pens */ "red SmartPen" = PEN (size = 7 color = "red" arrowsize = 7); "green SmartPen" = PEN (size = 7 color = "green" arrowsize = 7); "blue SmartPen" = PEN (size = 7 color = "blue" arrowsize = 7); /* Markers */ "red Marker" = RECOLOR (size = 15 color = "red"); "green Marker" = RECOLOR (size = 15 color = "green"); "blue Marker" = RECOLOR (size = 15 color = "blue"); # "yellow Marker" = RECOLOR (size = 15 color = "yellow"); /* Erasers */ "big Eraser" = ERASER (size = 75); "medium Eraser" = ERASER (size = 50); "small Eraser" = ERASER (size = 25); # "tiny Eraser" = ERASER (size = 10); /* Bindings */ # Lines in MB1, with shift it creates arrows "Core Pointer"[Button1] = "red Pen"; "Core Pointer"[Button1, CONTROL] = "green Pen"; "Core Pointer"[Button1, META] = "blue Pen"; "Core Pointer"[Button1, SHIFT] = "red SmartPen"; "Core Pointer"[Button1, SHIFT, CONTROL] = "green SmartPen"; "Core Pointer"[Button1, SHIFT, META] = "blue SmartPen"; # Recolor in MB2 "Core Pointer"[Button2] = "red Marker"; "Core Pointer"[Button2, CONTROL] = "green Marker"; "Core Pointer"[Button2, META] = "blue Marker"; # Erase in MB3 "Core Pointer"[Button3] = "big Eraser"; "Core Pointer"[Button3, CONTROL] = "medium Eraser"; "Core Pointer"[Button3, META] = "small Eraser"; # It paints pretty nice this way "WacomStylus0"[Button1] = "black Quill"; "WacomStylus0"[Button1, CONTROL] = "gray Quill"; "WacomStylus0"[Button1, META] = "white Quill"; # I have only one side button in the Wacom pen "WacomStylus0"[Button2] = "red Marker"; "WacomStylus0"[Button2, CONTROL] = "green Marker"; "WacomStylus0"[Button2, META] = "blue Marker"; # I use the eraser tip as MB4 "WacomStylus0"[Button4] = "big Eraser"; "WacomStylus0"[Button4, CONTROL] = "medium Eraser"; "WacomStylus0"[Button4, META] = "small Eraser"; # Or remapped to MB3 "WacomStylus0"[Button3] = "big Eraser"; "WacomStylus0"[Button3, CONTROL] = "medium Eraser"; "WacomStylus0"[Button3, META] = "small Eraser"; gromit-20041213/Makefile0000644000175000017500000000066210133323153015203 0ustar simonsimon00000000000000all: gromit proptest: proptest.c gcc -o proptest proptest.c `gtk-config --libs --cflags` propertywatch: propertywatch.c gcc -o propertywatch propertywatch.c `gtk-config --libs --cflags` gromit: gromit.c Makefile gcc -DG_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DPANGO_DISABLE_DEPRECATED -DGDK_MULTIHEAD_SAFE -DGTK_MULTIHEAD_SAFE -o gromit gromit.c -Wall `pkg-config --libs --cflags gtk+-2.0` gromit-20041213/paint_cursor_mask.xbm0000644000175000017500000000171107342200357020002 0ustar simonsimon00000000000000#define paint_cursor_mask_width 31 #define paint_cursor_mask_height 31 #define paint_cursor_mask_x_hot 14 #define paint_cursor_mask_y_hot 14 static unsigned char paint_cursor_mask_bits[] = { 0x00, 0x40, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0xf0, 0x1f, 0xfe, 0x03, 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; gromit-20041213/paint_cursor.xbm0000644000175000017500000000166007342200357016772 0ustar simonsimon00000000000000#define paint_cursor_width 31 #define paint_cursor_height 31 #define paint_cursor_x_hot 14 #define paint_cursor_y_hot 14 static unsigned char paint_cursor_bits[] = { 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; gromit-20041213/README0000644000175000017500000001352010157273607014436 0ustar simonsimon00000000000000Gromit ---------- Gromit (GRaphics Over MIscellaneous Things) is a small tool to make annotations on the screen. When making presentations of the Gimp I found myself often moving the mousepointer around the point of interest until hopefully everybody noticed it. This annoyed me, since it is a very vague way to highlight something. I dreamt of a small programm which allows me to simply draw on the screen, ignoring any window-borders. Gromit is a first implementation of this program. The main usage problem of Gromit is its activation. You need a special command to make Gromit grab the mouse, since you typically want to use the program you are demonstrating and highlighting something is a short interruption of your workflow. Gromit offers two ways to make this possible. It grabs the "Pause" key, so that no other application can use it and it is available to Gromit only. The available commands are: Pause: toggle painting SHIFT-Pause: clear screen CTRL-Pause: toggle visibility ALT-Pause: Quit Gromit. You can specify the key to grab via "gromit --key ". Specifying an empty string or "none" for the keysym will prevent gromit from grabbing a key. Alternatively you can invoke Gromit with various arguments to control an already running Gromit (If you are curious: Communication between two gromit instances is done via a special X-Selection). Usage: gromit --quit will cause the main Gromit process to quit (or "-q") gromit --toggle will toggle the grabbing of the cursor (or "-t") gromit --visibility will toggle the visibility of the window (or "-v") gromit --clear will clear the screen (or "-c") If activated Gromit prevents you from using other programs with the mouse. You can press the button and paint on the screen. Key presses (except the "Pause"-Key, see above) will still reach the currently active window but it may be difficult to change the window-focus without mouse... The next "gromit --toggle" will deactivate Gromit and you can use your programs as usual - only the painted regions will be obscured. Gromit is pressure sensitive, if you are using properly configured XInput-Devices you can draw lines with varying width. It is possible to erase something with the other end of the (Wacom) pen. Building: Gromit is small and lightwight. It needs GTK+ 2.X to build and the Makefile is straightforward. No need for autoconf/automake yet :-) Simply type "make" and copy the resulting binary to a convenient place. Stripping the binary can reduce its size. I just tested it on Linux/XFree86, reports from other platforms are welcome. Configuration: Gromit is configurable via the file ".gromitrc" in your Homedirectory. Here you can specify which Device/Button/Modifier combination invokes which tool. See the file "gromitconf" distributed with this program for an example. An overview on the syntax: # Comments can be either # Shell-Style or /* C-Style. */ This entry defines the tool "red Pen", a pen with size 7 and color red. You can specify the color in X-Style: e.g. "#FF0033" or colors from rgb.txt. "red Pen" = PEN (size=7 color="red"); The following Entries copy an existing configuration (in this case "red Pen") and modify the color. "blue Pen" = "red Pen" (color="blue"); "yellow Pen" = "red Pen" (color="yellow"); You can also draw lines that end in an arrow head. For this you have to specify "arrowsize". This is a factor relative to the width of the line. For reasonable arrowheads start with 1. "blue Pen" = "blue Arrow" (arrowsize=2); An "ERASER" is a tool that erases the drawings on screen. The color parameter is not important. "Eraser" = ERASER (size = 75); A "RECOLOR"-Tool changes the color of the drawing without changing the shape. Try it out to see the effect. "green Marker" = RECOLOR (color = "Limegreen"); If you define a tool with the same name as an input-device (see the output of "xsetpointer -l", if there is a "SWITCH"-Tool it is uninteresting...) this input-device uses this tool. Additionally you can limit the Scope to specific combinations of Mousebuttons (1,2,3,4,5 or Button1,...,Button5) and Modifiers (SHIFT, CONTROL, ALT, META, while ALT==META). "Core Pointer" = "red Pen"; "Core Pointer"[SHIFT] = "blue Pen"; "Core Pointer"[CONTROL] = "yellow Pen"; "Core Pointer"[2] = "green Marker"; "Core Pointer"[Button3] = "Eraser"; The descision, which tool to use follows a simple policy: a) Buttons are more important than Modifiers b) Low number Buttons are more important than higher ones c) Modifiers: SHIFT > CONTROL > ALT/META. d) Gromit tries partial matches: If you define "Core Pointer"[] and "Core Pointer"[SHIFT, CONTROL] and only SHIFT actually is pressed, Gromit will use the second definition if there is no "Core Pointer"[SHIFT] definition. Same logic goes for the buttons. Problems: Gromit may drastically slow down your X-Server, especially when you draw very thin lines. It makes heavily use of the shape extension, which is quite expensive if you paint a complex pattern on screen. Especially terminal-programs tend to scroll incredibly slow if something is painted over their window. There is nothing I can do about this. Gromit partially disables DnD, since it lays a transparent window across the whole screen and everything gets "dropped" to this (invisible) window. Gromit tries to minimize this effect: When you clear the screen the shaped window will be hidden. It will be resurrected, when you want to paint something again. However: The window does not hide, if you erase everything with the eraser tool, you have to clear the screen explicitely with the "gromit --clear" command or hide Gromit with "gromit --visibility". This Program is distributed under the Gnu General Public License. See the file COPYING for details. Have fun, Simon Budig gromit-20041213/ChangeLog0000644000175000017500000000266410157273663015341 0ustar simonsimon000000000000002004-12-13 Simon Budig * gromit.c: - Make activation on startup possible (gromit -a) - add configure option for the key to grab (gromit -k "Pause") * README: Adjusted. 2004-10-14 Simon Budig * gromit.c: - Fix pressure sensitivity - massive indentation madness. * README: Adjusted to the XGrabKey change. 2004-10-14 Simon Budig * gromit.c: - Use XGrabKey to grab the "Pause" key of the keyboard. Use it to control gromits functionality. The selection-hack is no longer needed (but still available). 2004-10-14 Simon Budig * gromit.c: - Applied a (slightly modified) patch from Bastien Nocera (hadess@hadess.net) to port gromit to GTK+ 2. Still needs some testing with a Tablet. - removed deprecated stuff - changed the activation of input devices, having mice as extended input devices apparently confuses GDK. 2003-03-04 Simon Budig * gromit.c: Make sure that the arrowhead is connected to the line. Draw black outline around the arrowhead. Have just one global last coordinate. Tackles some problems when switching tools while drawing. 2001-12-10 Simon Budig * README * gromit.c: Implemented Arrowheads, changed Name of resourcefile to "~/.gromitrc" 2001-02-26 Simon Budig * */* Sorry, no real changelog. 2000-08-28 Simon Budig * */* First version gromit-20041213/AUTHORS0000644000175000017500000000003607342200360014610 0ustar simonsimon00000000000000Simon Budig gromit-20041213/gromitrc0000644000175000017500000000134007342200357015316 0ustar simonsimon00000000000000# Configuration file for gromit # Tools "roter Stift" = PEN (size=7 color="red"); "blauer Stift" = "roter Stift" (color="blue"); "gelber Stift" = "roter Stift" (color="yellow"); "rosa Stift" = "roter Stift" (color="#ff00aa"); "Radierer" = ERASER (size = 75); "grüner Marker" = RECOLOR (color = "Limegreen"); # Mapping to Pointing devices "Core Pointer" = "roter Stift"; "Core Pointer"[SHIFT] = "blauer Stift"; "Core Pointer"[CONTROL] = "gelber Stift"; "Core Pointer"[META] = "rosa Stift"; "Core Pointer"[2] = "grüner Marker"; "Core Pointer"[3] = "Radierer"; "Core Pointer"[3 SHIFT] = "Radierer" (size = 150); "Pen1" = "roter Stift" (size = 15); "Pen1"[SHIFT] = "blauer Stift"; "Pen1"[CONTROL] = "Radierer"; "Eraser" = "Radierer";