1 /*
2 * keyboardcast.c - broadcast keystrokes to multiple windows
3 * Copyright © 2005 Ryan Lortie <desrt@desrt.ca>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110 USA
17 */
18
19 #include <glade/glade.h>
20 #include <gdk/gdkx.h>
21 #include <gtk/gtk.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "grab-window.h"
26 #include "window-list.h"
27
28 static WindowList *wl;
29 GladeXML *xml;
30
31 static gboolean
window_list_foreach_callback(WindowList * wl,int xid,gpointer user_data)32 window_list_foreach_callback( WindowList *wl, int xid, gpointer user_data )
33 {
34 XKeyEvent *xevent = user_data;
35
36 xevent->window = xid;
37 gdk_error_trap_push();
38 XSendEvent( xevent->display, xid, FALSE, KeyPressMask, (XEvent *) xevent );
39 gdk_flush();
40 gdk_error_trap_pop();
41
42 return FALSE;
43 }
44
45 static gboolean
key_event(GtkWidget * input,GdkEventKey * event)46 key_event( GtkWidget *input, GdkEventKey *event )
47 {
48 XKeyEvent xevent;
49
50 switch( event->type )
51 {
52 case GDK_KEY_PRESS:
53 xevent.type = KeyPress;
54 break;
55
56 case GDK_KEY_RELEASE:
57 xevent.type = KeyRelease;
58 break;
59
60 default:
61 return FALSE;
62 }
63
64 xevent.display = gdk_x11_get_default_xdisplay();
65 xevent.keycode = event->hardware_keycode;
66 xevent.state = event->state;
67 xevent.time = event->time;
68 xevent.root = gdk_x11_get_default_root_xwindow();
69
70 window_list_foreach_selected( wl, window_list_foreach_callback, &xevent );
71
72 return TRUE;
73 }
74
75 static void
expanded(GtkExpander * expander,GParamSpec * ps,GtkWindow * window)76 expanded( GtkExpander *expander, GParamSpec *ps, GtkWindow *window )
77 {
78 gtk_window_set_resizable( window, gtk_expander_get_expanded( expander ) );
79 }
80
81 static gboolean
selection_foreach(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)82 selection_foreach( GtkTreeModel *model, GtkTreePath *path,
83 GtkTreeIter *iter, gpointer data )
84 {
85 const char *criteria = data;
86 const char *name;
87 gboolean active;
88
89 gtk_tree_model_get( model, iter, 2, &name, -1 );
90
91 active = criteria && strstr( name, criteria );
92
93 gtk_list_store_set( GTK_LIST_STORE( model ), iter, 1, active, -1 );
94
95 return FALSE;
96 }
97
98 static void
spawn_command(const char * cmd,const char * args)99 spawn_command( const char *cmd, const char *args )
100 {
101 char **argv;
102 int i;
103
104 argv = g_strsplit( args, ",", 0 );
105
106 for( i = 0; argv[i]; i++ )
107 {
108 char *command;
109 command = g_strdup_printf( "gnome-terminal --window-with-profile=keyboardcast -t 'Keyboardcast Spawn' -e '%s %s'&", cmd, argv[i] );
110 system( command );
111 g_free( command );
112 }
113
114 g_strfreev( argv );
115 }
116
117 static void
update_label(GtkEntry * entry,GtkLabel * label)118 update_label( GtkEntry *entry, GtkLabel *label )
119 {
120 static char *original_label;
121 const char *args;
122 char *new_label;
123 char **argv;
124 int i;
125
126 if( !original_label )
127 original_label = g_strdup( gtk_label_get_label( label ) );
128
129 if( entry )
130 {
131 args = gtk_entry_get_text( entry );
132
133 /* do it this way so it's exactly the same as the other function */
134 argv = g_strsplit( args, ",", 0 );
135 for( i = 0; argv[i]; i++ );
136 g_strfreev( argv );
137 }
138 else
139 i = 4;
140
141 new_label = g_strdup_printf( original_label, i );
142 gtk_label_set_markup( label, new_label );
143 g_free( new_label );
144 }
145
146 static void
spawn_clicked(GtkButton * button,GtkDialog * spawn_dialog)147 spawn_clicked( GtkButton *button, GtkDialog *spawn_dialog )
148 {
149 GtkWidget *command, *arguments;
150 const char *cmd, *arg;
151 int response;
152
153 response = gtk_dialog_run( spawn_dialog );
154 gtk_widget_hide( GTK_WIDGET( spawn_dialog ) );
155
156 if( response != GTK_RESPONSE_OK )
157 return;
158
159 command = glade_xml_get_widget( xml, "command-entry" );
160 arguments = glade_xml_get_widget( xml, "arguments-entry" );
161
162 cmd = gtk_entry_get_text( GTK_ENTRY( command ) );
163 arg = gtk_entry_get_text( GTK_ENTRY( arguments ) );
164
165 spawn_command( cmd, arg );
166 }
167
168 static void
button_clicked(GtkButton * button,GtkEntry * select_entry)169 button_clicked( GtkButton *button, GtkEntry *select_entry )
170 {
171 const char *label = gtk_button_get_label( button );
172 const char *criteria;
173
174 if( !strcmp( label, "_Grab" ) )
175 {
176 window_list_select_xid( wl, grab_window_xid() );
177 return;
178 }
179
180 if( !strcmp( label, "_Select" ) )
181 criteria = gtk_entry_get_text( select_entry );
182 else if( !strcmp( label, "_None" ) )
183 criteria = NULL;
184 else if( !strcmp( label, "_All" ) )
185 criteria = "";
186 else
187 return;
188
189 gtk_tree_model_foreach( GTK_TREE_MODEL( wl ),selection_foreach,
190 (gpointer) criteria );
191 }
192
193 static void
terminal_toggled(GtkToggleButton * button)194 terminal_toggled( GtkToggleButton *button )
195 {
196 const char *process;
197
198 if( gtk_toggle_button_get_active( button ) )
199 process = "gnome-terminal";
200 else
201 process = NULL;
202
203 window_list_filter_by_process( wl, process );
204 }
205
206 static int
setup_interface(void)207 setup_interface( void )
208 {
209 GtkWidget *window, *treeview, *select_entry, *spawn_dialog, *spawn_label;
210 GtkCellRenderer *renderer;
211
212 gtk_window_set_default_icon_name( "gnome-dev-keyboard" );
213
214 wl = window_list_new();
215
216 xml = glade_xml_new( PREFIX "/share/keyboardcast/keyboardcast.glade",
217 NULL, NULL );
218
219 if( xml == NULL )
220 return 1;
221
222 select_entry = glade_xml_get_widget( xml, "select-entry" );
223 treeview = glade_xml_get_widget( xml, "treeview" );
224 window = glade_xml_get_widget( xml, "window" );
225 spawn_dialog = glade_xml_get_widget( xml, "spawn-dialog" );
226 spawn_label = glade_xml_get_widget( xml, "spawn-label" );
227
228 if( select_entry == NULL || treeview == NULL ||
229 window == NULL || spawn_dialog == NULL || spawn_label == NULL )
230 return 1;
231
232 gtk_tree_view_set_model( GTK_TREE_VIEW( treeview ), GTK_TREE_MODEL( wl ) );
233
234 renderer = window_list_toggle_renderer( wl );
235 gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( treeview ), -1,
236 "✓", renderer, // ☑☒✓✔
237 "active", 1, NULL );
238
239 renderer = gtk_cell_renderer_text_new();
240 gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( treeview ), -1,
241 "Window Title", renderer,
242 "text", 2, NULL );
243
244 glade_xml_signal_connect( xml, "key_event", G_CALLBACK( key_event ) );
245 glade_xml_signal_connect( xml, "gtk_main_quit", G_CALLBACK( gtk_main_quit ) );
246 glade_xml_signal_connect_data( xml, "expanded",
247 G_CALLBACK( expanded ), window );
248 glade_xml_signal_connect_data( xml, "button_clicked",
249 G_CALLBACK( button_clicked ), select_entry );
250 glade_xml_signal_connect_data( xml, "spawn_clicked",
251 G_CALLBACK( spawn_clicked ), spawn_dialog );
252 glade_xml_signal_connect( xml, "terminal_toggled",
253 G_CALLBACK( terminal_toggled ) );
254 glade_xml_signal_connect_data( xml, "update_label",
255 G_CALLBACK( update_label ), spawn_label );
256
257 update_label( NULL, GTK_LABEL( spawn_label ) );
258
259 window_list_filter_by_process( wl, "gnome-terminal" );
260
261 gtk_widget_show_all( window );
262
263 return 0;
264 }
265
266 int
main(int argc,char ** argv)267 main( int argc, char **argv )
268 {
269 int ret;
270
271 gtk_init( &argc, &argv );
272
273 if( !(ret = setup_interface()) )
274 gtk_main();
275
276 return ret;
277 }
278