2 * GNT - The GLib Ncurses Toolkit
4 * GNT is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 #if defined(__APPLE__) || defined(__unix__)
25 #define _XOPEN_SOURCE_EXTENDED
32 #include <sys/types.h>
37 #include "gntbutton.h"
38 #include "gntcolors.h"
39 #include "gntclipboard.h"
46 #include "gntwindow.h"
61 * Notes: Interesting functions to look at:
62 * scr_dump, scr_init, scr_restore: for workspaces
64 * Need to wattrset for colors to use with PDCurses.
67 static GIOChannel
*channel
= NULL
;
68 static int channel_read_callback
;
70 static gboolean ascii_only
;
71 static gboolean mouse_enabled
;
73 static void setup_io(void);
75 static gboolean
refresh_screen();
78 static GntClipboard
*clipboard
;
80 #define HOLDING_ESCAPE (escape_stuff.timer != 0)
87 escape_timeout(gpointer data
)
89 gnt_wm_process_input(wm
, "\033");
90 escape_stuff
.timer
= 0;
96 * - bring a window on top if you click on its taskbar
97 * - click on the top-bar of the active window and drag+drop to move a window
98 * - click on a window to bring it to focus
99 * - allow scrolling in tree/textview on wheel-scroll event
100 * - click to activate button or select a row in tree
102 * - have a little [X] on the windows, and clicking it will close that window.
105 detect_mouse_action(const char *buffer
)
113 } button
= MOUSE_NONE
;
114 static GntWidget
*remember
= NULL
;
115 static int offset
= 0;
117 GntWidget
*widget
= NULL
;
120 if (!wm
->cws
->ordered
|| buffer
[0] != 27)
124 if (strlen(buffer
) < 5)
134 while ((p
= panel_below(p
)) != NULL
) {
135 const GntNode
*node
= panel_userptr(p
);
140 if (x
>= wid
->priv
.x
&& x
< wid
->priv
.x
+ wid
->priv
.width
) {
141 if (y
>= wid
->priv
.y
&& y
< wid
->priv
.y
+ wid
->priv
.height
) {
148 if (strncmp(buffer
, "[M ", 3) == 0) {
149 /* left button down */
150 /* Bring the window you clicked on to front */
151 /* If you click on the topbar, then you can drag to move the window */
152 event
= GNT_LEFT_MOUSE_DOWN
;
153 } else if (strncmp(buffer
, "[M\"", 3) == 0) {
154 /* right button down */
155 event
= GNT_RIGHT_MOUSE_DOWN
;
156 } else if (strncmp(buffer
, "[M!", 3) == 0) {
157 /* middle button down */
158 event
= GNT_MIDDLE_MOUSE_DOWN
;
159 } else if (strncmp(buffer
, "[M`", 3) == 0) {
161 event
= GNT_MOUSE_SCROLL_UP
;
162 } else if (strncmp(buffer
, "[Ma", 3) == 0) {
164 event
= GNT_MOUSE_SCROLL_DOWN
;
165 } else if (strncmp(buffer
, "[M#", 3) == 0) {
167 event
= GNT_MOUSE_UP
;
171 if (widget
&& gnt_wm_process_click(wm
, event
, x
, y
, widget
))
174 if (event
== GNT_LEFT_MOUSE_DOWN
&& widget
&& widget
!= wm
->_list
.window
&&
175 !GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_TRANSIENT
)) {
176 if (widget
!= wm
->cws
->ordered
->data
) {
177 gnt_wm_raise_window(wm
, widget
);
179 if (y
== widget
->priv
.y
) {
180 offset
= x
- widget
->priv
.x
;
184 } else if (event
== GNT_MOUSE_UP
) {
185 if (button
== MOUSE_NONE
&& y
== getmaxy(stdscr
) - 1) {
186 /* Clicked on the taskbar */
187 int n
= g_list_length(wm
->cws
->list
);
189 int width
= getmaxx(stdscr
) / n
;
190 gnt_bindable_perform_action_named(GNT_BINDABLE(wm
), "switch-window-n", x
/width
, NULL
);
192 } else if (button
== MOUSE_LEFT
&& remember
) {
196 gnt_screen_move_widget(remember
, x
, y
);
204 gnt_widget_clicked(widget
, event
, x
, y
);
209 io_invoke_error(GIOChannel
*source
, GIOCondition cond
, gpointer data
)
211 int id
= GPOINTER_TO_INT(data
);
213 g_io_channel_unref(source
);
221 io_invoke(GIOChannel
*source
, GIOCondition cond
, gpointer null
)
228 if (wm
->mode
== GNT_KP_MODE_WAIT_ON_CHILD
)
231 rd
= read(STDIN_FILENO
, keys
+ HOLDING_ESCAPE
, sizeof(keys
) - 1 - HOLDING_ESCAPE
);
234 int ch
= getch(); /* This should return ERR, but let's see what it really returns */
236 printf("ERROR: %s\n", strerror(errno
));
237 printf("File descriptor is: %d\n\nGIOChannel is: %p\ngetch() = %d\n", STDIN_FILENO
, source
, ch
);
247 rd
+= HOLDING_ESCAPE
;
251 gnt_wm_set_event_stack(wm
, TRUE
);
253 cvrt
= g_locale_to_utf8(keys
, rd
, (gsize
*)&rd
, NULL
, NULL
);
254 k
= cvrt
? cvrt
: keys
;
255 if (mouse_enabled
&& detect_mouse_action(k
))
259 /* I am not sure what's happening here. If this actually does something,
260 * then this needs to go in gnt_keys_refine. */
261 if (*k
< 0) { /* Alt not sending ESC* */
273 if (k
[0] == '\033' && rd
== 1) {
274 if (escape_stuff
.timer
) {
275 gnt_wm_process_input(wm
, "\033\033");
276 g_source_remove(escape_stuff
.timer
);
277 escape_stuff
.timer
= 0;
280 escape_stuff
.timer
= g_timeout_add(250, escape_timeout
, NULL
);
285 p
= MAX(1, gnt_keys_find_combination(k
));
288 gnt_wm_process_input(wm
, k
); /* XXX: */
295 gnt_wm_set_event_stack(wm
, FALSE
);
304 channel
= g_io_channel_unix_new(STDIN_FILENO
);
305 g_io_channel_set_close_on_unref(channel
, TRUE
);
308 g_io_channel_set_encoding(channel
, NULL
, NULL
);
309 g_io_channel_set_buffered(channel
, FALSE
);
310 g_io_channel_set_flags(channel
, G_IO_FLAG_NONBLOCK
, NULL
);
313 channel_read_callback
= result
= g_io_add_watch_full(channel
, G_PRIORITY_HIGH
,
314 (G_IO_IN
| G_IO_HUP
| G_IO_ERR
| G_IO_PRI
),
315 io_invoke
, NULL
, NULL
);
317 g_io_add_watch_full(channel
, G_PRIORITY_HIGH
,
319 io_invoke_error
, GINT_TO_POINTER(result
), NULL
);
321 g_io_channel_unref(channel
); /* Apparently this caused crashes for some people.
322 But irssi does this, so I am going to assume the
323 crashes were caused by some other stuff. */
325 g_printerr("gntmain: setting up IO (%d)\n", channel_read_callback
);
331 gnt_bindable_perform_action_named(GNT_BINDABLE(wm
), "refresh-screen", NULL
);
343 pid
= waitpid(-1, &status
, WNOHANG
);
344 } while (pid
!= 0 && pid
!= (pid_t
)-1);
346 if ((pid
== (pid_t
) - 1) && (errno
!= ECHILD
)) {
348 g_snprintf(errmsg
, BUFSIZ
, "Warning: waitpid() returned %d", pid
);
354 exit_confirmed(gpointer null
)
356 gnt_bindable_perform_action_named(GNT_BINDABLE(wm
), "wm-quit", NULL
);
360 exit_win_close(GntWidget
*w
, GntWidget
**win
)
368 static GntWidget
*win
= NULL
;
369 GntWidget
*bbox
, *button
;
373 gnt_widget_hide(GNT_WIDGET(wm
->menu
));
375 wm
->menu
= wm
->menu
->parentmenu
;
382 win
= gnt_vwindow_new(FALSE
);
383 gnt_box_add_widget(GNT_BOX(win
), gnt_label_new("Are you sure you want to quit?"));
384 gnt_box_set_title(GNT_BOX(win
), "Quit?");
385 gnt_box_set_alignment(GNT_BOX(win
), GNT_ALIGN_MID
);
386 g_signal_connect(G_OBJECT(win
), "destroy", G_CALLBACK(exit_win_close
), &win
);
388 bbox
= gnt_hbox_new(FALSE
);
389 gnt_box_add_widget(GNT_BOX(win
), bbox
);
391 button
= gnt_button_new("Quit");
392 g_signal_connect(G_OBJECT(button
), "activate", G_CALLBACK(exit_confirmed
), NULL
);
393 gnt_box_add_widget(GNT_BOX(bbox
), button
);
395 button
= gnt_button_new("Cancel");
396 g_signal_connect_swapped(G_OBJECT(button
), "activate", G_CALLBACK(gnt_widget_destroy
), win
);
397 gnt_box_add_widget(GNT_BOX(bbox
), button
);
399 gnt_widget_show(win
);
401 gnt_wm_raise_window(wm
, win
);
405 static void (*org_winch_handler
)(int);
415 g_idle_add(refresh_screen
, NULL
);
416 if (org_winch_handler
)
417 org_winch_handler(sig
);
418 signal(SIGWINCH
, sighandler
);
423 signal(SIGCHLD
, sighandler
);
427 signal(SIGINT
, sighandler
);
435 const char *name
= gnt_style_get(GNT_STYLE_WM
);
439 handle
= g_module_open(name
, G_MODULE_BIND_LAZY
);
441 gboolean (*init
)(GntWM
**);
442 if (g_module_symbol(handle
, "gntwm_init", (gpointer
)&init
)) {
448 wm
= g_object_new(GNT_TYPE_WM
, NULL
);
459 locale
= setlocale(LC_ALL
, "");
466 if (locale
&& (strstr(locale
, "UTF") || strstr(locale
, "utf")))
480 filename
= g_build_filename(g_get_home_dir(), ".gntrc", NULL
);
481 gnt_style_read_configure_file(filename
);
486 wbkgdset(stdscr
, '\0' | gnt_color_pair(GNT_COLOR_NORMAL
));
489 #ifdef ALL_MOUSE_EVENTS
490 if ((mouse_enabled
= gnt_style_get_bool(GNT_STYLE_MOUSE
, FALSE
)))
491 mousemask(ALL_MOUSE_EVENTS
| REPORT_MOUSE_POSITION
, NULL
);
494 wbkgdset(stdscr
, '\0' | gnt_color_pair(GNT_COLOR_NORMAL
));
499 org_winch_handler
= signal(SIGWINCH
, sighandler
);
501 signal(SIGCHLD
, sighandler
);
502 signal(SIGINT
, sighandler
);
503 signal(SIGPIPE
, SIG_IGN
);
509 clipboard
= g_object_new(GNT_TYPE_CLIPBOARD
, NULL
);
514 wm
->loop
= g_main_loop_new(NULL
, FALSE
);
515 g_main_loop_run(wm
->loop
);
518 /*********************************
519 * Stuff for 'window management' *
520 *********************************/
522 void gnt_window_present(GntWidget
*window
)
525 gnt_wm_raise_window(wm
, window
);
527 gnt_widget_set_urgent(window
);
530 void gnt_screen_occupy(GntWidget
*widget
)
532 gnt_wm_new_window(wm
, widget
);
535 void gnt_screen_release(GntWidget
*widget
)
538 gnt_wm_window_close(wm
, widget
);
541 void gnt_screen_update(GntWidget
*widget
)
543 gnt_wm_update_window(wm
, widget
);
546 gboolean
gnt_widget_has_focus(GntWidget
*widget
)
552 if (GNT_IS_MENU(widget
))
557 while (widget
->parent
)
558 widget
= widget
->parent
;
560 if (widget
== wm
->_list
.window
)
562 if (wm
->cws
->ordered
&& wm
->cws
->ordered
->data
== widget
) {
563 if (GNT_IS_BOX(widget
) &&
564 (GNT_BOX(widget
)->active
== w
|| widget
== w
))
570 void gnt_widget_set_urgent(GntWidget
*widget
)
572 while (widget
->parent
)
573 widget
= widget
->parent
;
575 if (wm
->cws
->ordered
&& wm
->cws
->ordered
->data
== widget
)
578 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_URGENT
);
580 gnt_wm_update_window(wm
, widget
);
585 g_object_unref(G_OBJECT(wm
));
595 gboolean
gnt_ascii_only()
600 void gnt_screen_resize_widget(GntWidget
*widget
, int width
, int height
)
602 gnt_wm_resize_window(wm
, widget
, width
, height
);
605 void gnt_screen_move_widget(GntWidget
*widget
, int x
, int y
)
607 gnt_wm_move_window(wm
, widget
, x
, y
);
610 void gnt_screen_rename_widget(GntWidget
*widget
, const char *text
)
612 gnt_box_set_title(GNT_BOX(widget
), text
);
613 gnt_widget_draw(widget
);
614 gnt_wm_update_window(wm
, widget
);
617 void gnt_register_action(const char *label
, void (*callback
)())
619 GntAction
*action
= g_new0(GntAction
, 1);
620 action
->label
= g_strdup(label
);
621 action
->callback
= callback
;
623 wm
->acts
= g_list_append(wm
->acts
, action
);
627 reset_menu(GntWidget
*widget
, gpointer null
)
629 g_object_unref(G_OBJECT(wm
->menu
));
633 gboolean
gnt_screen_menu_show(gpointer newmenu
)
636 /* For now, if a menu is being displayed, then another menu
637 * can NOT take over. */
642 GNT_WIDGET_UNSET_FLAGS(GNT_WIDGET(wm
->menu
), GNT_WIDGET_INVISIBLE
);
643 gnt_widget_draw(GNT_WIDGET(wm
->menu
));
644 g_object_ref(G_OBJECT(wm
->menu
));
646 g_signal_connect(G_OBJECT(wm
->menu
), "hide", G_CALLBACK(reset_menu
), NULL
);
647 g_signal_connect(G_OBJECT(wm
->menu
), "destroy", G_CALLBACK(reset_menu
), NULL
);
652 void gnt_set_clipboard_string(const gchar
*string
)
654 gnt_clipboard_set_string(clipboard
, string
);
657 GntClipboard
*gnt_get_clipboard()
662 gchar
*gnt_get_clipboard_string()
664 return gnt_clipboard_get_string(clipboard
);
667 #if GLIB_CHECK_VERSION(2,4,0)
670 void (*callback
)(int status
, gpointer data
);
675 reap_child(GPid pid
, gint status
, gpointer data
)
677 ChildProcess
*cp
= data
;
679 cp
->callback(status
, cp
->data
);
683 wm
->mode
= GNT_KP_MODE_NORMAL
;
690 gboolean
gnt_giveup_console(const char *wd
, char **argv
, char **envp
,
691 gint
*stin
, gint
*stout
, gint
*sterr
,
692 void (*callback
)(int status
, gpointer data
), gpointer data
)
694 #if GLIB_CHECK_VERSION(2,4,0)
696 ChildProcess
*cp
= NULL
;
698 if (!g_spawn_async_with_pipes(wd
, argv
, envp
,
699 G_SPAWN_SEARCH_PATH
| G_SPAWN_DO_NOT_REAP_CHILD
,
700 (GSpawnChildSetupFunc
)endwin
, NULL
,
701 &pid
, stin
, stout
, sterr
, NULL
))
704 cp
= g_new0(ChildProcess
, 1);
705 cp
->callback
= callback
;
707 g_source_remove(channel_read_callback
);
708 wm
->mode
= GNT_KP_MODE_WAIT_ON_CHILD
;
709 g_child_watch_add(pid
, reap_child
, cp
);
717 gboolean
gnt_is_refugee()
719 #if GLIB_CHECK_VERSION(2,4,0)
720 return (wm
&& wm
->mode
== GNT_KP_MODE_WAIT_ON_CHILD
);