1 
2 /*
3  * glspi_app.c - This file is part of the Lua scripting plugin for the Geany IDE
4  * See the file "geanylua.c" for copyright information.
5  */
6 
7 #ifdef HAVE_CONFIG_H
8 #	include "config.h"
9 #endif
10 
11 #include <sys/stat.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <errno.h>
15 
16 
17 #define NEED_FAIL_ARG_TYPE
18 #include "glspi.h"
19 
20 
glspi_pluginver(lua_State * L)21 static gint glspi_pluginver(lua_State* L)
22 {
23 	lua_pushfstring(L, _(
24 "%s %s: %s\n"
25 "Copyright (c) 2007-2010 "PLUGIN_AUTHOR", et al.\n"
26 "Compiled on "__DATE__" at "__TIME__" for Geany API version %d\n"
27 "Released under version 2 of the GNU General Public License.\n"
28 	),
29 			PLUGIN_NAME, PLUGIN_VER, PLUGIN_DESC, MY_GEANY_API_VER);
30 	return 1;
31 }
32 
33 
glspi_tools(lua_State * L)34 static gint glspi_tools(lua_State* L)
35 {
36 	lua_newtable(L);
37 	SetTableStr("browser", geany_data->tool_prefs->browser_cmd);
38 	SetTableStr("term",    geany_data->tool_prefs->term_cmd);
39 	SetTableStr("grep",    geany_data->tool_prefs->grep_cmd);
40 	SetTableStr("action",  geany_data->tool_prefs->context_action_cmd);
41 	return 1;
42 }
43 
44 
glspi_template(lua_State * L)45 static gint glspi_template(lua_State* L)
46 {
47 	lua_newtable(L);
48 	SetTableStr("developer", geany_data->template_prefs->developer);
49 	SetTableStr("company",   geany_data->template_prefs->company);
50 	SetTableStr("mail",      geany_data->template_prefs->mail);
51 	SetTableStr("initial",   geany_data->template_prefs->initials);
52 	SetTableStr("version",   geany_data->template_prefs->version);
53 	return 1;
54 }
55 
56 
57 
glspi_project(lua_State * L)58 static gint glspi_project(lua_State* L)
59 {
60 	GeanyProject *project = geany->app->project;
61 
62 	if (project) {
63 		lua_newtable(L);
64 		SetTableStr("name", project->name);
65 		SetTableStr("desc", project->description);
66 		SetTableStr("file", project->file_name);
67 		SetTableStr("base", project->base_path);
68 		if (project->file_patterns && *project->file_patterns) {
69 			gchar *tmp=g_strjoinv(";", project->file_patterns);
70 			SetTableStr("mask", tmp);
71 			g_free(tmp);
72 		}
73 		return 1;
74 	} else {
75 		return 0;
76 	}
77 }
78 
79 static const gchar *glspi_script_dir = NULL;
80 
glspi_appinfo(lua_State * L)81 static gint glspi_appinfo(lua_State* L)
82 {
83 	lua_newtable(L);
84 	SetTableBool("debug", geany->app->debug_mode);
85 	SetTableStr("configdir", geany->app->configdir);
86 	SetTableStr("datadir", geany->app->datadir);
87 	SetTableStr("docdir", geany->app->docdir);
88 	SetTableStr("scriptdir", glspi_script_dir);
89 	lua_pushstring(L,"template");
90 	glspi_template(L);
91 	lua_rawset(L,1);
92 
93 	lua_pushstring(L,"tools");
94 	glspi_tools(L);
95 	lua_rawset(L,1);
96 
97 	if (geany->app->project) {
98 		lua_pushstring(L,"project");
99 		glspi_project(L);
100 		lua_rawset(L,1);
101 	}
102 	return 1;
103 }
104 
105 
106 
107 #ifndef G_OS_WIN32
108 
109 #define CLIPBOARD gtk_clipboard_get(GDK_SELECTION_PRIMARY)
110 
glspi_xsel(lua_State * L)111 static gint glspi_xsel(lua_State* L)
112 {
113 	if (lua_gettop(L)>0) {
114 		if (lua_isstring(L,1)) {
115 			gsize len;
116 			const gchar*txt=lua_tolstring(L,1,&len);
117 			gtk_clipboard_set_text(CLIPBOARD,txt,len);
118 		} else {
119 			FAIL_STRING_ARG(1);
120 		}
121 		return 0;
122 	} else {
123 		gchar *txt=gtk_clipboard_wait_for_text(CLIPBOARD);
124 		if (txt) {
125 			lua_pushstring(L,txt);
126 			g_free(txt);
127 		} else {
128 			lua_pushstring(L,"");
129 		}
130 		return 1;
131 	}
132 }
133 
134 #else
glspi_xsel(lua_State * L)135 static gint glspi_xsel(lua_State* L) { return 0; }
136 #endif
137 
138 
139 
140 
glspi_signal(lua_State * L)141 static gint glspi_signal(lua_State* L) {
142 	const gchar*widname,*signame;
143 	GtkWidget*w;
144 	GType typeid;
145 	guint sigid;
146 	if ((lua_gettop(L)<2)||!lua_isstring(L,2) ) {	return FAIL_STRING_ARG(2); }
147 	if (!lua_isstring(L,1) ) {	return FAIL_STRING_ARG(1); }
148 	widname=lua_tostring(L,1);
149 	signame=lua_tostring(L,2);
150 	w=ui_lookup_widget(main_widgets->window, widname);
151 	if (!w) {
152 		lua_pushfstring(L, _("Error in module \"%s\" at function %s():\n"
153 			"widget \"%s\" not found for argument #1.\n"),
154 			LUA_MODULE_NAME, &__FUNCTION__[6], widname);
155 			lua_error(L);
156 		return 0;
157 	}
158 	typeid=G_OBJECT_TYPE(w);
159 	sigid=g_signal_lookup(signame,typeid);
160 	if (!sigid) {
161 		lua_pushfstring(L, _("Error in module \"%s\" at function %s() argument #2:\n"
162 			"widget \"%s\" has no signal named \"%s\".\n"),
163 			LUA_MODULE_NAME, &__FUNCTION__[6], widname, signame);
164 			lua_error(L);
165 		return 0;
166 	}
167 
168 	g_signal_emit(w, sigid, 0);
169 	return 0;
170 }
171 
172 
173 
174 
175 #ifdef G_OS_WIN32
176 #define lstat stat
177 #include <io.h>
178 #endif
179 
180 typedef int (*statfunc) (const char *fn, struct stat *st);
181 
glspi_stat(lua_State * L)182 static gint glspi_stat(lua_State* L)
183 {
184 	statfunc sf=stat;
185 	const gchar*fn=NULL;
186 	struct stat st;
187 	if (lua_gettop(L)<1) { return FAIL_STRING_ARG(1); }
188 	if (lua_gettop(L)>=2) {
189 		if (!lua_isboolean(L,2)) { return FAIL_BOOL_ARG(2); }
190 		sf=lua_toboolean(L,2)?lstat:stat;
191 	}
192 	if (!lua_isstring(L,1)) { return FAIL_STRING_ARG(1); }
193 	fn=lua_tostring(L,1);
194 	if (sf(fn,&st)==0) {
195 		const gchar *ft=NULL;
196 		switch ( st.st_mode & S_IFMT) {
197 			case S_IFBLK:ft="b"; break;
198 			case S_IFCHR:ft="c"; break;
199 			case S_IFDIR:ft="d"; break;
200 			case S_IFIFO:ft="f"; break;
201 			case S_IFREG:ft="r"; break;
202 #ifndef G_OS_WIN32
203 			case S_IFLNK:ft="l"; break;
204 			case S_IFSOCK:ft="s"; break;
205 #endif
206 		}
207 		lua_newtable(L);
208 		SetTableNum("size",st.st_size);
209 		SetTableNum("time",st.st_mtime);
210 		SetTableStr("type",ft);
211 		SetTableBool("read", (access(fn,R_OK)==0));
212 		SetTableBool("write", (access(fn,W_OK)==0));
213 		SetTableBool("exec", (access(fn,X_OK)==0));
214 		return 1;
215 	}
216 	lua_pushnil(L);
217 	lua_pushstring(L, strerror(errno));
218 	return 2;
219 }
220 
221 
glspi_basename(lua_State * L)222 static gint glspi_basename(lua_State* L)
223 {
224 	if (lua_gettop(L)>=1) {
225 		gchar *bn=NULL;
226 		const gchar *fn=NULL;
227 		if (!lua_isstring(L,1)) { return FAIL_STRING_ARG(1); }
228 		fn=lua_tostring(L,1);
229 		bn=g_path_get_basename(fn);
230 		lua_pushstring(L,bn);
231 		g_free(bn);
232 		return 1;
233 	}
234 	return 0;
235 }
236 
237 
glspi_dirname(lua_State * L)238 static gint glspi_dirname(lua_State* L)
239 {
240 	if (lua_gettop(L)>=1) {
241 		gchar *dn=NULL;
242 		const gchar *fn=NULL;
243 		if (!lua_isstring(L,1)) { return FAIL_STRING_ARG(1); }
244 		fn=lua_tostring(L,1);
245 		dn=g_path_get_dirname(fn);
246 		lua_pushstring(L,dn);
247 		g_free(dn);
248 		return 1;
249 	}
250 	return 0;
251 }
252 
253 
254 
glspi_fullpath(lua_State * L)255 static gint glspi_fullpath(lua_State* L)
256 {
257 	if (lua_gettop(L)>=1) {
258 		gchar *rp=NULL;
259 		const gchar *fn=NULL;
260 		if (!lua_isstring(L,1)) { return FAIL_STRING_ARG(1); }
261 		fn=lua_tostring(L,1);
262 		rp=utils_get_real_path(fn);
263 		if (rp) {
264 			lua_pushstring(L,rp);
265 			g_free(rp);
266 			return 1;
267 		}
268 	}
269 	return 0;
270 }
271 
272 
273 
dirlist_closure(lua_State * L)274 static gint dirlist_closure(lua_State *L)
275 {
276 	GDir*dir=lua_touserdata(L,lua_upvalueindex(1));
277 	const gchar*entry=g_dir_read_name(dir);
278 	if (entry) {
279 		lua_pushstring(L,entry);
280 		return 1;
281 	} else {
282 		g_dir_close(dir);
283 		return 0;
284 	}
285 }
286 
287 
glspi_status(lua_State * L)288 static gint glspi_status(lua_State* L)
289 {
290 	const gchar *string = lua_tostring(L,1);
291 
292 	msgwin_status_add("%s", string);
293 
294 	return 0;
295 }
296 
297 
glspi_dirlist(lua_State * L)298 static gint glspi_dirlist(lua_State* L)
299 {
300 	GDir*dir=NULL;
301 	const gchar*dn=".";
302 	GError*err=NULL;
303 	if (lua_gettop(L)>=1) {
304 		if (!lua_isstring(L,1)) { return FAIL_STRING_ARG(1); }
305 		dn=lua_tostring(L,1);
306 	}
307 	dir=g_dir_open(dn,0,&err);
308 	if (dir) {
309 		lua_pushlightuserdata(L,dir);
310 		lua_pushcclosure(L,&dirlist_closure,1);
311 		return 1;
312 	} else {
313 		lua_pushfstring(L, "Error in module \"%s\" at function %s() argument #2\n%s",
314 		LUA_MODULE_NAME, &__FUNCTION__[6],err?err->message:"Error reading directory."
315 		);
316 		if (err) {g_error_free(err);}
317 		lua_error(L);
318 		return 0;
319 	}
320 	return 0;
321 }
322 
323 
324 
glspi_wkdir(lua_State * L)325 static gint glspi_wkdir(lua_State* L)
326 {
327 	if (lua_gettop(L)== 0 ) {
328 		gchar*wd=getcwd(NULL,0);
329 		if (wd) {
330 			lua_pushstring(L,wd);
331 			free(wd);
332 			return 1;
333 		} else { return 0; }
334 	} else {
335 		if (!lua_isstring(L,1)) { return FAIL_STRING_ARG(1); }
336 		if ( chdir(lua_tostring(L,1)) == 0 ) {
337 			lua_pushboolean(L,TRUE);
338 			return 1;
339 		} else {
340 			lua_pushboolean(L,FALSE);
341 			lua_pushstring(L, strerror(errno));
342 			return 2;
343 		}
344 	}
345 }
346 
347 
348 #include "glspi_keycmd.h"
349 
350 static GHashTable*key_cmd_hash=NULL;
351 
glspi_init_key_cmd_hash(void)352 static void glspi_init_key_cmd_hash(void)
353 {
354 	gint i;
355 	key_cmd_hash=g_hash_table_new(g_str_hash,g_str_equal);
356 	for (i=0;key_cmd_hash_entries[i].name; i++) {
357 		g_hash_table_insert(
358 			key_cmd_hash,(gpointer) key_cmd_hash_entries[i].name,
359 			&key_cmd_hash_entries[i]);
360 	}
361 }
362 
363 
glspi_free_key_cmd_hash(void)364 static void glspi_free_key_cmd_hash(void) {
365 	if (key_cmd_hash) {
366 		g_hash_table_destroy(key_cmd_hash);
367 		key_cmd_hash=NULL;
368 	}
369 }
370 
glspi_set_key_cmd_hash(gboolean create)371 void glspi_set_key_cmd_hash(gboolean create) {
372 	if (create) {
373 		glspi_init_key_cmd_hash ();
374 	} else {
375 		glspi_free_key_cmd_hash();
376 	}
377 }
378 
379 
380 
381 #define lookup_key_cmd_str(cmd) g_hash_table_lookup(key_cmd_hash,cmd);
382 
383 
glspi_keycmd(lua_State * L)384 static gint glspi_keycmd(lua_State* L)
385 {
386 	KeyCmdHashEntry*he=NULL;
387 	if (lua_gettop(L)<1) {return FAIL_STRING_ARG(1); }
388 	if (lua_isstring(L,1)) {
389 		gchar cmdbuf[64];
390 		gchar *cmdname;
391 		gint i;
392 		memset(cmdbuf,'\0', sizeof(cmdbuf));
393 		strncpy(cmdbuf,lua_tostring(L,1),sizeof(cmdbuf)-1);
394 		for (i=0;cmdbuf[i];i++) {cmdbuf[i]=g_ascii_toupper(cmdbuf[i]);}
395 		cmdname=cmdbuf;
396 		if (strncmp(cmdname,"GEANY_",6)==0) {
397 			cmdname+=6;
398 			if (strncmp(cmdname,"KEYS_",5)==0) {
399 				cmdname+=5;
400 			}
401 		}
402 		he=lookup_key_cmd_str(cmdname);
403 	} else { return FAIL_STRING_ARG(1); }
404 	if ( !he ) {
405 		lua_pushfstring(
406 			L, _( "Error in module \"%s\" at function %s():\n"
407 				"unknown command \"%s\" given for argument #1.\n"),
408 			LUA_MODULE_NAME, &__FUNCTION__[6], lua_tostring(L,1));
409 		lua_error(L);
410 		return 0;
411 	}
412 	keybindings_send_command(he->group, he->key_id);
413 	return 0;
414 }
415 
416 
glspi_launch(lua_State * L)417 static gint glspi_launch(lua_State* L)
418 {
419 	gint argc=lua_gettop(L);
420 	gint i;
421 	gchar **argv=NULL;
422 	gboolean rv;
423 	GError *err=NULL;
424 	if (argc==0) { return FAIL_STRING_ARG(1); }
425 	for (i=1;i<=argc;i++) {
426 		if (!lua_isstring(L,i)) { return FAIL_STRING_ARG(i); }
427 	}
428 	argv=g_malloc0(sizeof(gchar *)*argc+1);
429 	for (i=0;i<argc;i++) {
430 		argv[i]=(g_strdup(lua_tostring(L,i+1)));
431 	}
432 	rv=g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err);
433 	g_strfreev(argv);
434 	lua_pushboolean(L,rv);
435 	if (rv) { return 1; }
436 	lua_pushstring(L,err->message);
437 	g_error_free(err);
438 	return 2;
439 }
440 
441 
442 static guint My_Shift_L=0;
443 static guint My_Shift_R=0;
444 static guint My_Control_L=0;
445 static guint My_Control_R=0;
446 static guint My_Alt_L=0;
447 static guint My_Alt_R=0;
448 
449 
450 #ifndef G_OS_WIN32
451 
452 #include <X11/Xlib.h>
453 #include <X11/keysym.h>
454 
455 
456 #define IsShift ( (My_Shift_L == ev->xkey.keycode) || (My_Shift_R == ev->xkey.keycode) )
457 
458 #define IsCtrl ( (My_Control_L == ev->xkey.keycode) || (My_Control_R == ev->xkey.keycode) )
459 #define IsAlt ( (My_Alt_L == ev->xkey.keycode) || (My_Alt_R == ev->xkey.keycode) )
460 
461 #define IsCtrlAlt ( IsCtrl || IsAlt )
462 
463 typedef struct _KeyGrabData {
464 gchar *prompt;
465 GdkKeymapKey km;
466 } _KeyGrabData;
467 
468 
keygrab_cb(GdkXEvent * xevent,GdkEvent * event,gpointer data)469 static GdkFilterReturn keygrab_cb(GdkXEvent *xevent, GdkEvent *event, gpointer data)
470 {
471 	XEvent*ev = (XEvent*) xevent;
472 	GdkKeymapKey *km = (GdkKeymapKey*) data;
473 	switch (ev->type) {
474 		case KeyPress:{
475 			if (IsShift) {
476 				km->level=1;
477 			} else {
478 				if (!IsCtrlAlt) km->group=1; /* Flag to know we have keydown before keyup */
479 			}
480 			return GDK_FILTER_REMOVE;
481 		}
482 		case KeyRelease:{
483 			if (IsShift) {
484 				km->level=0;
485 			} else {
486 				if ((km->group==1)&&(!IsCtrlAlt)) { /* OK, we already got our keydown  */
487 					km->group=2;
488 					km->level=(ev->xkey.state & ShiftMask)?1:0;
489 					km->keycode=ev->xkey.keycode;
490 				}
491 			}
492 			return GDK_FILTER_REMOVE;
493 		}
494 		default:{}
495 	}
496 	return GDK_FILTER_CONTINUE;
497 }
498 
499 #define dosleep() g_usleep(1)
500 
501 #else
502 #include <windows.h>
503 #define dosleep() Sleep(1)
504 
505 #define IsShift ( (My_Shift_L == msg->wParam) || (My_Shift_R == msg->wParam) )
506 
507 #define IsCtrl ( (My_Control_L == msg->wParam) || (My_Control_R == msg->wParam) )
508 #define IsAlt ( (My_Alt_L == msg->wParam) || (My_Alt_R == msg->wParam) )
509 
510 #define IsCtrlAlt ( IsCtrl || IsAlt )
511 
512 
keygrab_cb(GdkXEvent * xevent,GdkEvent * event,gpointer data)513 static GdkFilterReturn keygrab_cb(GdkXEvent *xevent, GdkEvent *event, gpointer data)
514 {
515 	MSG*msg = (MSG*) xevent;
516 	GdkKeymapKey *km = (GdkKeymapKey*) data;
517 	switch (msg->message) {
518 		case WM_KEYDOWN:{
519 			if (IsShift) {
520 				km->level=1;
521 			} else {
522 				if (!IsCtrlAlt) km->group=1; /* Flag to know we have keydown before keyup */
523 			}
524 			return GDK_FILTER_REMOVE;
525 		}
526 		case WM_KEYUP:{
527 			if (IsShift) {
528 				km->level=0;
529 			} else {
530 				if ((km->group==1)&&(!IsCtrlAlt)) { /* OK, we already got our keydown  */
531 					km->group=2;
532 					km->level=HIBYTE(GetKeyState(VK_SHIFT))?1:0;
533 					km->keycode=msg->wParam;
534 				}
535 			}
536 			return GDK_FILTER_REMOVE;
537 		}
538 		default:{}
539 	}
540 	return GDK_FILTER_CONTINUE;
541 }
542 
543 
544 #endif
545 
546 
547 #include <gdk/gdkkeysyms.h>
init_key(guint keyval)548 static gint init_key(guint keyval){
549 	GdkKeymapKey *kmk=NULL;
550 	GdkKeymap *gdk_key_map=gdk_keymap_get_default();
551 	gint n_keys=0;
552 	gint rv=0;
553 	if (gdk_keymap_get_entries_for_keyval(gdk_key_map,keyval,&kmk,&n_keys)) {
554 		rv=kmk[0].keycode;
555 		g_free(kmk);
556 	}
557 	return rv;
558 }
559 
560 #define InitKey(code,value) if (!code) { code=init_key(value); }
561 
glspi_keygrab(lua_State * L)562 static gint glspi_keygrab(lua_State* L)
563 {
564 	GeanyDocument*doc=NULL;
565 	const gchar*prompt=NULL;
566 	GdkKeymapKey km={0,0,0};
567 	GdkKeymap *gdk_key_map;
568 	km.keycode=0;
569 	km.group=0; /* Note: we hijack this field to use as a flag for first keydown. */
570 	km.level=0;
571 	InitKey(My_Shift_L, GDK_Shift_L);
572 	InitKey(My_Shift_R, GDK_Shift_R);
573 	InitKey(My_Control_L, GDK_Control_L);
574 	InitKey(My_Control_R, GDK_Control_R);
575 	InitKey(My_Alt_L, GDK_Alt_L);
576 	InitKey(My_Alt_R, GDK_Alt_R);
577 	if (lua_gettop(L)>0) {
578 		if (!lua_isstring(L,1)) {return FAIL_STRING_ARG(1); }
579 		prompt=lua_tostring(L,1);
580 		doc=document_get_current();
581 	}
582 
583 	if (prompt && doc && doc->is_valid ) {
584 		gint fvl=scintilla_send_message(doc->editor->sci,SCI_GETFIRSTVISIBLELINE, 0,0);
585 		gint pos=sci_get_position_from_line(doc->editor->sci, fvl+1);
586 		scintilla_send_message(doc->editor->sci,SCI_CALLTIPSHOW,pos+3, (sptr_t)prompt);
587 	}
588 	gdk_window_add_filter(gtk_widget_get_window(main_widgets->window), keygrab_cb, &km);
589 	do {
590 		while (gtk_events_pending()) {
591 			if (km.group==2) { break; }
592 			gtk_main_iteration();
593 		}
594 		if (km.group==2) { break; }
595 		dosleep();
596 	} while (km.group!=2);
597 
598 	gdk_window_remove_filter(gtk_widget_get_window(main_widgets->window), keygrab_cb, &km);
599 	if (prompt && doc && doc->is_valid) {
600 	sci_send_command(doc->editor->sci, SCI_CALLTIPCANCEL);
601 	}
602 	km.group=0; /* reset the hijacked flag before passing to GDK */
603 	gdk_key_map = gdk_keymap_get_default();
604 	lua_pushstring(L, gdk_keyval_name(gdk_keymap_lookup_key(gdk_key_map, &km)));
605 
606 	return 1;
607 }
608 
609 
glspi_reloadconf(lua_State * L)610 static gint glspi_reloadconf(lua_State* L)
611 {
612 	main_reload_configuration();
613 	return 0;
614 }
615 
616 
617 
618 static const struct luaL_reg glspi_app_funcs[] = {
619 	{"pluginver",  glspi_pluginver},
620 	{"appinfo",    glspi_appinfo},
621 	{"xsel",       glspi_xsel},
622 	{"signal",     glspi_signal},
623 	{"stat",       glspi_stat},
624 	{"status",     glspi_status},
625 	{"basename",   glspi_basename},
626 	{"dirname",    glspi_dirname},
627 	{"fullpath",   glspi_fullpath},
628 	{"dirlist",    glspi_dirlist},
629 	{"wkdir",      glspi_wkdir},
630 	{"keycmd",     glspi_keycmd},
631 	{"launch",     glspi_launch},
632 	{"keygrab",    glspi_keygrab},
633 	{"reloadconf", glspi_reloadconf},
634 	{NULL,NULL}
635 };
636 
glspi_init_app_funcs(lua_State * L,const gchar * script_dir)637 void glspi_init_app_funcs(lua_State *L, const gchar*script_dir) {
638 	glspi_script_dir = script_dir;
639 	luaL_register(L, NULL,glspi_app_funcs);
640 }
641