Compare commits

...

10 Commits

13 changed files with 1436 additions and 685 deletions

31
COPYING
View File

@ -1,3 +1,6 @@
Copyright (c) 2013-2018, Pierre Pronchery <khorben@defora.org>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
@ -6,21 +9,17 @@ met:
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice this list of conditions and the following disclaimer in the
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the authors nor the names of the contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,2 +1,5 @@
../src/main.c
../src/tab.c
../src/terminal.c
../src/widget.c
../src/window.c

44
src/common.h Normal file
View File

@ -0,0 +1,44 @@
/* $Id$ */
/* Copyright (c) 2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Terminal */
/* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. */
#ifndef TERMINAL_COMMON_H
# define TERMINAL_COMMON_H
# include <gtk/gtk.h>
# include <System.h>
/* TerminalCommon */
/* public */
/* types */
typedef struct _Terminal Terminal;
typedef struct _TerminalTab TerminalTab;
typedef struct _TerminalWidget TerminalWidget;
typedef struct _TerminalWindow TerminalWindow;
#endif /* !TERMINAL_COMMON_H */

View File

@ -1,5 +1,5 @@
/* $Id$ */
/* Copyright (c) 2012-2016 Pierre Pronchery <khorben@defora.org> */
/* Copyright (c) 2012-2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Terminal */
/* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -12,10 +12,6 @@
* notice this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the authors nor the names of the contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
@ -42,17 +38,17 @@
#define _(string) gettext(string)
/* constants */
#ifndef PROGNAME
# define PROGNAME "terminal"
#ifndef PROGNAME_TERMINAL
# define PROGNAME_TERMINAL "terminal"
#endif
#ifndef PREFIX
# define PREFIX "/usr/local"
# define PREFIX "/usr/local"
#endif
#ifndef DATADIR
# define DATADIR PREFIX "/share"
# define DATADIR PREFIX "/share"
#endif
#ifndef LOCALEDIR
# define LOCALEDIR DATADIR "/locale"
# define LOCALEDIR DATADIR "/locale"
#endif
@ -89,7 +85,7 @@ static int _terminal(TerminalPrefs * prefs)
/* error */
static int _error(char const * message, int ret)
{
fputs(PROGNAME ": ", stderr);
fputs(PROGNAME_TERMINAL ": ", stderr);
perror(message);
return ret;
}
@ -98,7 +94,8 @@ static int _error(char const * message, int ret)
/* usage */
static int _usage(void)
{
fprintf(stderr, _("Usage: %s [-d directory][shell]\n"), PROGNAME);
fprintf(stderr, _("Usage: %s [-d directory][shell]\n"),
PROGNAME_TERMINAL);
return 1;
}

View File

@ -1,19 +1,38 @@
targets=terminal
#cppflags=-D EMBEDDED
cflags_force=`pkg-config --cflags libDesktop`
cflags=-W -Wall -g -O2 -pedantic -fPIE -D_FORTIFY_SOURCE=2 -fstack-protector
ldflags_force=`pkg-config --libs libDesktop` -lintl
ldflags=-pie -Wl,-z,relro -Wl,-z,now
#for XTerm
#cppflags_force=-DWITH_XTERM
#cflags_force=`pkg-config --cflags libDesktop`
#ldflags_force=`pkg-config --libs libDesktop` -lintl
#for Gtk+ 2 (VTE)
#cflags_force=`pkg-config --cflags libDesktop vte`
#ldflags_force=`pkg-config --libs libDesktop vte` -lintl
#for Gtk+ 3 (VTE)
cflags_force=`pkg-config --cflags libDesktop vte-2.91`
ldflags_force=`pkg-config --libs libDesktop vte-2.91` -lintl
dist=Makefile,terminal.h
#targets
[terminal]
type=binary
sources=terminal.c,main.c
sources=tab.c,terminal.c,widget.c,window.c,main.c
install=$(BINDIR)
#sources
[main.c]
depends=terminal.h,../config.h
[tab.c]
depends=tab.h
[terminal.c]
depends=terminal.h,../config.h
cppflags=-D PREFIX=\"$(PREFIX)\"
[main.c]
depends=terminal.h,../config.h
[widget.c]
depends=widget.h
[window.c]
depends=window.h

329
src/tab.c Normal file
View File

@ -0,0 +1,329 @@
/* $Id$ */
/* Copyright (c) 2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Terminal */
/* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. */
#include <stdio.h>
#include <libintl.h>
#include <gtk/gtk.h>
#include <sys/wait.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#ifdef WITH_XTERM
# if GTK_CHECK_VERSION(3, 0, 0)
# include <gtk/gtkx.h>
# endif
#else
# include <vte/vte.h>
#endif
#include <System.h>
#include <Desktop.h>
#include "terminal.h"
#include "window.h"
#include "tab.h"
#define _(string) gettext(string)
/* constants */
#ifndef PROGNAME_TERMINAL
# define PROGNAME_TERMINAL "terminal"
#endif
#ifdef WITH_XTERM
# ifndef PROGNAME_XTERM
# define PROGNAME_XTERM "xterm"
# endif
#endif
#ifndef PREFIX
# define PREFIX "/usr/local"
#endif
#ifndef BINDIR
# define BINDIR PREFIX "/bin"
#endif
/* TerminalTab */
/* private */
/* types */
struct _TerminalTab
{
Terminal * terminal;
GtkWidget * widget;
GtkWidget * label;
GtkWidget * socket;
GPid pid;
guint source;
};
/* prototypes */
/* callbacks */
static void _terminaltab_on_child_watch(GPid pid, gint status, gpointer data);
static void _terminaltab_on_close(gpointer data);
static void _terminaltab_on_rename(gpointer data);
static void _terminaltab_on_screen_changed(GtkWidget * widget,
GdkScreen * screen, gpointer data);
/* public */
/* functions */
/* terminaltab_new */
TerminalTab * terminaltab_new(Terminal * terminal)
{
TerminalTab * tab;
GtkWidget * widget;
if((tab = object_new(sizeof(*tab))) == NULL)
return NULL;
tab->terminal = terminal;
tab->pid = -1;
tab->source = 0;
#ifdef WITH_XTERM
tab->socket = gtk_socket_new();
#else
tab->socket = vte_terminal_new();
#endif
g_signal_connect(tab->socket, "screen-changed", G_CALLBACK(
_terminaltab_on_screen_changed), tab);
tab->widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
tab->label = gtk_label_new(_("xterm"));
gtk_box_pack_start(GTK_BOX(tab->widget), tab->label, TRUE, TRUE, 0);
widget = gtk_button_new();
g_signal_connect_swapped(widget, "clicked", G_CALLBACK(
_terminaltab_on_rename), tab);
gtk_container_add(GTK_CONTAINER(widget), gtk_image_new_from_stock(
GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU));
gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE);
gtk_box_pack_start(GTK_BOX(tab->widget), widget, FALSE, TRUE, 0);
widget = gtk_button_new();
g_signal_connect_swapped(widget, "clicked", G_CALLBACK(
_terminaltab_on_close), tab);
gtk_container_add(GTK_CONTAINER(widget), gtk_image_new_from_stock(
GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU));
gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE);
gtk_box_pack_start(GTK_BOX(tab->widget), widget, FALSE, TRUE, 0);
gtk_widget_show_all(tab->widget);
gtk_widget_show(tab->socket);
return tab;
}
/* terminaltab_delete */
void terminaltab_delete(TerminalTab * tab)
{
if(tab->source > 0)
g_source_remove(tab->source);
if(tab->pid > 0)
{
g_spawn_close_pid(tab->pid);
if(kill(tab->pid, SIGTERM) != 0)
fprintf(stderr, "%s: %s: %s\n", PROGNAME_TERMINAL,
"kill", strerror(errno));
}
object_delete(tab);
}
/* accessors */
/* terminaltab_get_label */
GtkWidget * terminaltab_get_label(TerminalTab * tab)
{
return tab->widget;
}
/* terminaltab_get_widget */
GtkWidget * terminaltab_get_widget(TerminalTab * tab)
{
return tab->socket;
}
/* useful */
/* terminaltab_clipboard_copy */
void terminaltab_clipboard_copy(TerminalTab * tab)
{
#ifdef WITH_XTERM
#else
vte_terminal_copy_clipboard(VTE_TERMINAL(tab->socket));
#endif
}
/* terminaltab_clipboard_paste */
void terminaltab_clipboard_paste(TerminalTab * tab)
{
#ifdef WITH_XTERM
#else
vte_terminal_paste_clipboard(VTE_TERMINAL(tab->socket));
#endif
}
/* private */
/* functions */
/* callbacks */
/* terminaltab_on_child_watch */
static void _terminaltab_on_child_watch(GPid pid, gint status, gpointer data)
{
TerminalTab * tab = data;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%d, %d)\n", __func__, pid, status);
#endif
if(tab->pid != pid)
return;
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) != 0)
fprintf(stderr, "%s: %s%u\n", PROGNAME_TERMINAL,
_("process exited with status "),
WEXITSTATUS(status));
g_spawn_close_pid(tab->pid);
tab->pid = -1;
terminal_tab_close(tab->terminal, tab);
}
else if(WIFSIGNALED(status))
{
fprintf(stderr, "%s: %s%u\n", PROGNAME_TERMINAL,
_("process exited with signal "),
WTERMSIG(status));
g_spawn_close_pid(tab->pid);
tab->pid = -1;
terminal_tab_close(tab->terminal, tab);
}
}
/* terminaltab_on_close */
static void _terminaltab_on_close(gpointer data)
{
TerminalTab * tab = data;
terminal_tab_close(tab->terminal, tab);
}
/* terminaltab_on_rename */
static void _terminaltab_on_rename(gpointer data)
{
TerminalTab * tab = data;
TerminalWindow * twindow;
GtkWidget * window;
GtkWidget * dialog;
GtkWidget * content;
GtkWidget * entry;
gchar const * p;
twindow = terminal_get_window(tab->terminal);
window = terminalwindow_get_window(twindow);
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_OTHER, GTK_BUTTONS_NONE,
#if GTK_CHECK_VERSION(2, 6, 0)
"%s", _("Rename tab"));
# if GTK_CHECK_VERSION(2, 10, 0)
gtk_message_dialog_set_image(GTK_MESSAGE_DIALOG(dialog),
gtk_image_new_from_stock(GTK_STOCK_EDIT,
GTK_ICON_SIZE_DIALOG));
# endif
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
#endif
"%s", _("Rename this tab as:"));
gtk_dialog_add_buttons(GTK_DIALOG(dialog),
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
gtk_window_set_title(GTK_WINDOW(dialog), _("Rename tab"));
#if GTK_CHECK_VERSION(2, 14, 0)
content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
#else
content = GTK_DIALOG(dialog)->vbox;
#endif
entry = gtk_entry_new();
gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
gtk_entry_set_text(GTK_ENTRY(entry), gtk_label_get_text(
GTK_LABEL(tab->label)));
gtk_box_pack_start(GTK_BOX(content), entry, FALSE, TRUE, 0);
gtk_widget_show_all(content);
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
{
p = gtk_entry_get_text(GTK_ENTRY(entry));
gtk_label_set_text(GTK_LABEL(tab->label), p);
}
gtk_widget_destroy(dialog);
}
/* terminaltab_on_screen_changed */
static void _terminaltab_on_screen_changed(GtkWidget * widget,
GdkScreen * screen, gpointer data)
{
TerminalTab * tab = data;
const GSpawnFlags flags = G_SPAWN_FILE_AND_ARGV_ZERO
| G_SPAWN_DO_NOT_REAP_CHILD;
GError * error = NULL;
gboolean res;
#ifdef WITH_XTERM
char * argv[] = { BINDIR "/" PROGNAME_XTERM, PROGNAME_XTERM,
"-into", NULL, "-class", "Terminal", NULL, NULL, NULL };
char buf[32];
if(screen != NULL)
return;
/* launch xterm */
snprintf(buf, sizeof(buf), "%lu", gtk_socket_get_id(
GTK_SOCKET(widget)));
argv[3] = buf;
if(terminal_is_login(tab->terminal))
{
argv[6] = "-ls";
argv[7] = terminal_get_shell(tab->terminal);
}
else
argv[6] = terminal_get_shell(tab->terminal);
res = g_spawn_async(terminal_get_directory(tab->terminal), argv, NULL,
flags, NULL, NULL, &tab->pid, &error);
#else
char * argv[] = { NULL, NULL, NULL };
if((argv[0] = vte_get_user_shell()) == NULL)
argv[0] = "/bin/sh";
argv[1] = g_path_get_basename(argv[0]);
res = vte_terminal_spawn_sync(VTE_TERMINAL(tab->socket),
VTE_PTY_DEFAULT, NULL, argv, NULL, flags, NULL, NULL,
&tab->pid, NULL, &error);
g_free(argv[1]);
#endif
if(res == FALSE)
{
fprintf(stderr, "%s: %s: %s\n", PROGNAME_TERMINAL, argv[0],
error->message);
g_error_free(error);
return;
}
tab->source = g_child_watch_add(tab->pid, _terminaltab_on_child_watch,
tab);
}

49
src/tab.h Normal file
View File

@ -0,0 +1,49 @@
/* $Id$ */
/* Copyright (c) 2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Terminal */
/* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. */
#ifndef TERMINAL_TAB_H
# define TERMINAL_TAB_H
# include "common.h"
/* TerminalTab */
/* public */
/* functions */
TerminalTab * terminaltab_new(Terminal * terminal);
void terminaltab_delete(TerminalTab * tab);
/* accessors */
GtkWidget * terminaltab_get_label(TerminalTab * tab);
GtkWidget * terminaltab_get_widget(TerminalTab * tab);
/* useful */
void terminaltab_clipboard_copy(TerminalTab * tab);
void terminaltab_clipboard_paste(TerminalTab * tab);
#endif /* !TERMINAL_TAB_H */

View File

@ -1,203 +1,68 @@
/* $Id$ */
static char const _copyright[] =
"Copyright © 2012-2018 Pierre Pronchery <khorben@defora.org>";
/* Copyright (c) 2012-2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Terminal */
static char const _license[] =
"Redistribution and use in source and binary forms, with or without\n"
"modification, are permitted provided that the following conditions are\n"
"met:\n"
"\n"
"1. Redistributions of source code must retain the above copyright\n"
" notice, this list of conditions and the following disclaimer.\n"
"\n"
"2. Redistributions in binary form must reproduce the above copyright\n"
" notice this list of conditions and the following disclaimer in the\n"
" documentation and/or other materials provided with the distribution.\n"
"\n"
"3. Neither the name of the authors nor the names of the contributors may\n"
" be used to endorse or promote products derived from this software\n"
" without specific prior written permission.\n"
"\n"
"THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS \"AS IS\" AND\n"
"ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n"
"PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS\n"
"BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
"CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
"SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
"INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n"
"CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n"
"ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n"
"THE POSSIBILITY OF SUCH DAMAGE.";
/* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. */
/* TODO:
* - figure how to handle X resources */
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <libintl.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#if GTK_CHECK_VERSION(3, 0, 0)
# include <gtk/gtkx.h>
#endif
#include <System.h>
#include <Desktop.h>
#include "tab.h"
#include "widget.h"
#include "window.h"
#include "terminal.h"
#include "../config.h"
#define _(string) gettext(string)
#define N_(string) (string)
/* constants */
#ifndef PROGNAME
# define PROGNAME "terminal"
#ifndef PROGNAME_TERMINAL
# define PROGNAME_TERMINAL "terminal"
#endif
#ifndef PREFIX
# define PREFIX "/usr/local"
# define PREFIX "/usr/local"
#endif
#ifndef BINDIR
# define BINDIR PREFIX "/bin"
# define BINDIR PREFIX "/bin"
#endif
/* Terminal */
/* private */
/* types */
typedef struct _TerminalTab TerminalTab;
struct _Terminal
{
char * shell;
char * directory;
String * shell;
String * directory;
unsigned int login;
/* internal */
TerminalTab ** tabs;
size_t tabs_cnt;
/* widgets */
GtkWidget * window;
gboolean fullscreen;
#ifndef EMBEDDED
GtkWidget * menubar;
#endif
GtkToolItem * tb_fullscreen;
GtkWidget * notebook;
};
struct _TerminalTab
{
Terminal * terminal;
GtkWidget * widget;
GtkWidget * label;
GtkWidget * socket;
GPid pid;
guint source;
};
/* constants */
#ifndef EMBEDDED
static char const * _authors[] =
{
"Pierre Pronchery <khorben@defora.org>",
NULL
};
#endif
/* prototypes */
/* useful */
static int _terminal_open_tab(Terminal * terminal);
static int _terminal_open_window(Terminal * terminal);
static void _terminal_close_tab(Terminal * terminal, unsigned int i);
static void _terminal_close_all(Terminal * terminal);
/* callbacks */
static void _terminal_on_child_watch(GPid pid, gint status, gpointer data);
static void _terminal_on_close(gpointer data);
static gboolean _terminal_on_closex(gpointer data);
static void _terminal_on_fullscreen(gpointer data);
static void _terminal_on_new_tab(gpointer data);
static void _terminal_on_new_window(gpointer data);
static void _terminal_on_tab_close(gpointer data);
static void _terminal_on_tab_rename(gpointer data);
#ifndef EMBEDDED
static void _terminal_on_file_close(gpointer data);
static void _terminal_on_file_close_all(gpointer data);
static void _terminal_on_file_new_tab(gpointer data);
static void _terminal_on_file_new_window(gpointer data);
static void _terminal_on_view_fullscreen(gpointer data);
static void _terminal_on_help_about(gpointer data);
static void _terminal_on_help_contents(gpointer data);
#endif
/* constants */
#ifndef EMBEDDED
/* menubar */
static const DesktopMenu _terminal_file_menu[] =
{
{ N_("New _tab"), G_CALLBACK(_terminal_on_file_new_tab), "tab-new",
GDK_CONTROL_MASK, GDK_KEY_T },
{ N_("_New window"), G_CALLBACK(_terminal_on_file_new_window),
"window-new", GDK_CONTROL_MASK, GDK_KEY_N },
{ "", NULL, NULL, 0, 0 },
{ N_("_Close"), G_CALLBACK(_terminal_on_file_close), GTK_STOCK_CLOSE,
GDK_CONTROL_MASK, GDK_KEY_W },
{ N_("Close all tabs"), G_CALLBACK(_terminal_on_file_close_all), NULL,
GDK_SHIFT_MASK | GDK_CONTROL_MASK, GDK_KEY_W },
{ NULL, NULL, NULL, 0, 0 }
};
static const DesktopMenu _terminal_view_menu[] =
{
{ N_("_Fullscreen"), G_CALLBACK(_terminal_on_view_fullscreen),
# if GTK_CHECK_VERSION(2, 8, 0)
GTK_STOCK_FULLSCREEN,
# else
NULL,
# endif
0, GDK_KEY_F11 },
{ NULL, NULL, NULL, 0, 0 }
};
static const DesktopMenu _terminal_help_menu[] =
{
{ N_("_Contents"), G_CALLBACK(_terminal_on_help_contents),
"help-contents", 0, GDK_KEY_F1 },
#if GTK_CHECK_VERSION(2, 6, 0)
{ N_("_About"), G_CALLBACK(_terminal_on_help_about), GTK_STOCK_ABOUT, 0,
0 },
#else
{ N_("_About"), G_CALLBACK(_terminal_on_help_about), NULL, 0, 0 },
#endif
{ NULL, NULL, NULL, 0, 0 }
};
static const DesktopMenubar _terminal_menubar[] =
{
{ N_("_File"), _terminal_file_menu },
{ N_("_View"), _terminal_view_menu },
{ N_("_Help"), _terminal_help_menu },
{ NULL, NULL }
};
#endif
static DesktopToolbar _terminal_toolbar[] =
{
{ N_("New tab"), G_CALLBACK(_terminal_on_new_tab), "tab-new", 0, 0,
NULL },
{ N_("New window"), G_CALLBACK(_terminal_on_new_window), "window-new",
0, 0, NULL },
{ "", NULL, NULL, 0, 0, NULL },
{ NULL, NULL, NULL, 0, 0, NULL }
TerminalWindow * window;
};
@ -207,10 +72,6 @@ static DesktopToolbar _terminal_toolbar[] =
Terminal * terminal_new(TerminalPrefs * prefs)
{
Terminal * terminal;
GtkAccelGroup * group;
GtkWidget * vbox;
GtkWidget * widget;
GtkToolItem * toolitem;
if((terminal = object_new(sizeof(*terminal))) == NULL)
return NULL;
@ -219,61 +80,18 @@ Terminal * terminal_new(TerminalPrefs * prefs)
terminal->directory = (prefs != NULL && prefs->directory != NULL)
? string_new(prefs->directory) : NULL;
terminal->login = (prefs != NULL) ? prefs->login : 0;
terminal->tabs = NULL;
terminal->tabs_cnt = 0;
terminal->window = NULL;
terminal->fullscreen = FALSE;
/* check for errors */
if((prefs != NULL && prefs->shell != NULL && terminal->shell == NULL)
|| (prefs != NULL && prefs->directory != NULL
&& terminal->directory == NULL))
&& terminal->directory == NULL)
|| (terminal->window = terminalwindow_new(terminal))
== NULL)
{
terminal_delete(terminal);
return NULL;
}
/* widgets */
group = gtk_accel_group_new();
terminal->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_add_accel_group(GTK_WINDOW(terminal->window), group);
g_object_unref(group);
gtk_window_set_default_size(GTK_WINDOW(terminal->window), 600, 400);
#if GTK_CHECK_VERSION(2, 6, 0)
gtk_window_set_icon_name(GTK_WINDOW(terminal->window), "terminal");
#endif
gtk_window_set_title(GTK_WINDOW(terminal->window), _("Terminal"));
g_signal_connect_swapped(terminal->window, "delete-event", G_CALLBACK(
_terminal_on_closex), terminal);
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
#ifndef EMBEDDED
/* menubar */
terminal->menubar = desktop_menubar_create(_terminal_menubar, terminal,
group);
gtk_box_pack_start(GTK_BOX(vbox), terminal->menubar, FALSE, TRUE, 0);
#endif
/* toolbar */
widget = desktop_toolbar_create(_terminal_toolbar, terminal, group);
#if GTK_CHECK_VERSION(2, 8, 0)
toolitem = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_FULLSCREEN);
#else
toolitem = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_ZOOM_FIT);
#endif
terminal->tb_fullscreen = toolitem;
g_signal_connect_swapped(G_OBJECT(toolitem), "toggled", G_CALLBACK(
_terminal_on_fullscreen), terminal);
gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1);
gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
/* view */
terminal->notebook = gtk_notebook_new();
gtk_notebook_set_scrollable(GTK_NOTEBOOK(terminal->notebook), TRUE);
gtk_box_pack_start(GTK_BOX(vbox), terminal->notebook, TRUE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(terminal->window), vbox);
gtk_widget_show_all(vbox);
if(_terminal_open_tab(terminal) != 0)
{
terminal_delete(terminal);
return NULL;
}
gtk_widget_show(terminal->window);
terminal_tab_open(terminal);
return terminal;
}
@ -281,20 +99,8 @@ Terminal * terminal_new(TerminalPrefs * prefs)
/* terminal_delete */
void terminal_delete(Terminal * terminal)
{
size_t i;
for(i = 0; i < terminal->tabs_cnt; i++)
{
if(terminal->tabs[i]->source > 0)
g_source_remove(terminal->tabs[i]->source);
if(terminal->tabs[i]->pid > 0)
g_spawn_close_pid(terminal->tabs[i]->pid);
free(terminal->tabs[i]);
}
/* FIXME also take care of the sub-processes */
if(terminal->window != NULL)
gtk_widget_destroy(terminal->window);
free(terminal->tabs);
terminalwindow_delete(terminal->window);
string_delete(terminal->directory);
string_delete(terminal->shell);
object_delete(terminal);
@ -302,435 +108,81 @@ void terminal_delete(Terminal * terminal)
/* accessors */
/* terminal_set_fullscreen */
void terminal_set_fullscreen(Terminal * terminal, gboolean fullscreen)
/* terminal_get_directory */
char const * terminal_get_directory(Terminal * terminal)
{
if(fullscreen)
{
#ifndef EMBEDDED
gtk_widget_hide(terminal->menubar);
#endif
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(
terminal->tb_fullscreen), TRUE);
gtk_window_fullscreen(GTK_WINDOW(terminal->window));
}
else
{
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(
terminal->tb_fullscreen), FALSE);
gtk_window_unfullscreen(GTK_WINDOW(terminal->window));
#ifndef EMBEDDED
gtk_widget_show(terminal->menubar);
#endif
}
terminal->fullscreen = fullscreen;
return terminal->directory;
}
/* terminal_get_shell */
char const * terminal_get_shell(Terminal * terminal)
{
return terminal->shell;
}
/* terminal_get_window */
TerminalWindow * terminal_get_window(Terminal * terminal)
{
return terminal->window;
}
/* terminal_is_login */
gboolean terminal_is_login(Terminal * terminal)
{
return (terminal->login > 0) ? TRUE : FALSE;
}
/* private */
/* functions */
/* useful */
/* terminal_open_tab */
static int _terminal_open_tab(Terminal * terminal)
/* terminal_tab_close */
void terminal_tab_close(Terminal * terminal, TerminalTab * tab)
{
TerminalTab ** p;
TerminalTab * tab;
GtkWidget * widget;
char * argv[] = { BINDIR "/xterm", "xterm", "-into", NULL,
"-class", "Terminal", NULL, NULL, NULL };
char buf[32];
GSpawnFlags flags = G_SPAWN_FILE_AND_ARGV_ZERO
| G_SPAWN_DO_NOT_REAP_CHILD;
GError * error = NULL;
TerminalWidget * widget;
if((p = realloc(terminal->tabs, sizeof(*p) * (terminal->tabs_cnt + 1)))
== NULL)
return -1;
terminal->tabs = p;
if((tab = malloc(sizeof(*tab))) == NULL)
return -1;
terminal->tabs[terminal->tabs_cnt++] = tab;
/* create the tab */
tab->terminal = terminal;
tab->socket = gtk_socket_new();
tab->widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
tab->label = gtk_label_new(_("xterm"));
gtk_box_pack_start(GTK_BOX(tab->widget), tab->label, TRUE, TRUE, 0);
widget = gtk_button_new();
g_signal_connect_swapped(widget, "clicked", G_CALLBACK(
_terminal_on_tab_rename), tab);
gtk_container_add(GTK_CONTAINER(widget), gtk_image_new_from_stock(
GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU));
gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE);
gtk_box_pack_start(GTK_BOX(tab->widget), widget, FALSE, TRUE, 0);
widget = gtk_button_new();
g_signal_connect_swapped(widget, "clicked", G_CALLBACK(
_terminal_on_tab_close), tab);
gtk_container_add(GTK_CONTAINER(widget), gtk_image_new_from_stock(
GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU));
gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE);
gtk_box_pack_start(GTK_BOX(tab->widget), widget, FALSE, TRUE, 0);
gtk_widget_show_all(tab->widget);
gtk_notebook_append_page(GTK_NOTEBOOK(terminal->notebook), tab->socket,
tab->widget);
#if GTK_CHECK_VERSION(2, 10, 0)
gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(terminal->notebook),
tab->socket, TRUE);
#endif
/* launch xterm */
snprintf(buf, sizeof(buf), "%lu", gtk_socket_get_id(
GTK_SOCKET(tab->socket)));
argv[3] = buf;
if(terminal->login)
{
argv[6] = "-ls";
argv[7] = terminal->shell;
}
else
argv[6] = terminal->shell;
if(g_spawn_async(terminal->directory, argv, NULL, flags, NULL, NULL,
&tab->pid, &error) == FALSE)
{
fprintf(stderr, "%s: %s: %s\n", PROGNAME, argv[1],
error->message);
g_error_free(error);
return -1;
}
tab->source = g_child_watch_add(tab->pid, _terminal_on_child_watch,
terminal);
gtk_widget_show(tab->socket);
return 0;
widget = terminalwindow_get_widget(terminal->window);
terminalwidget_tab_close(widget, tab);
}
/* terminal_open_window */
static int _terminal_open_window(Terminal * terminal)
/* terminal_tab_close_all */
void terminal_tab_close_all(Terminal * terminal)
{
char * argv[] = { BINDIR "/" PROGNAME, PROGNAME, NULL };
TerminalWidget * widget;
widget = terminalwindow_get_widget(terminal->window);
terminalwidget_tab_close_all(widget);
}
/* terminal_tab_open */
TerminalTab * terminal_tab_open(Terminal * terminal)
{
TerminalWidget * widget;
widget = terminalwindow_get_widget(terminal->window);
return terminalwidget_tab_open(widget);
}
/* terminal_window_open */
int terminal_window_open(Terminal * terminal)
{
char * argv[] = { BINDIR "/" PROGNAME_TERMINAL, PROGNAME_TERMINAL,
NULL };
GSpawnFlags flags = G_SPAWN_FILE_AND_ARGV_ZERO;
GError * error = NULL;
if(g_spawn_async(NULL, argv, NULL, flags, NULL, NULL, NULL, &error)
== FALSE)
{
fprintf(stderr, "%s: %s: %s\n", PROGNAME, argv[0],
/* XXX use the current window or a dialog box */
fprintf(stderr, "%s: %s: %s\n", PROGNAME_TERMINAL, argv[0],
error->message);
g_error_free(error);
return -1;
}
return 0;
}
/* terminal_close_all */
static void _terminal_close_all(Terminal * terminal)
{
GPid * pid;
size_t i;
size_t cnt;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
gtk_widget_hide(terminal->window);
/* work on a copy */
if((pid = malloc(sizeof(*pid) * terminal->tabs_cnt)) == NULL)
{
gtk_main_quit();
return;
}
cnt = terminal->tabs_cnt;
for(i = 0; i < cnt; i++)
{
pid[i] = terminal->tabs[i]->pid;
g_spawn_close_pid(terminal->tabs[i]->pid);
terminal->tabs[i]->pid = -1;
}
/* kill the remaining tabs */
for(i = 0; i < cnt; i++)
if(kill(pid[i], SIGTERM) != 0)
fprintf(stderr, "%s: %s: %s\n", PROGNAME, "kill",
strerror(errno));
free(pid);
gtk_main_quit();
}
/* terminal_close_tab */
static void _terminal_close_tab(Terminal * terminal, unsigned int i)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%u)\n", __func__, i);
#endif
if(terminal->tabs[i]->source > 0)
g_source_remove(terminal->tabs[i]->source);
if(terminal->tabs[i]->pid >= 0)
{
g_spawn_close_pid(terminal->tabs[i]->pid);
if(kill(terminal->tabs[i]->pid, SIGTERM) != 0)
fprintf(stderr, "%s: %s: %s\n", PROGNAME, "kill",
strerror(errno));
}
free(terminal->tabs[i]);
gtk_notebook_remove_page(GTK_NOTEBOOK(terminal->notebook), i);
memmove(&terminal->tabs[i], &terminal->tabs[i + 1],
(terminal->tabs_cnt - (i + 1))
* sizeof(*terminal->tabs));
if(--terminal->tabs_cnt == 0)
gtk_main_quit();
}
/* callbacks */
/* terminal_on_child_watch */
static void _terminal_on_child_watch(GPid pid, gint status, gpointer data)
{
Terminal * terminal = data;
size_t i;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%d, %d)\n", __func__, pid, status);
#endif
for(i = 0; i < terminal->tabs_cnt; i++)
if(terminal->tabs[i]->pid == pid)
break;
if(i >= terminal->tabs_cnt)
return;
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) != 0)
fprintf(stderr, "%s: %s%u\n", PROGNAME,
_("xterm exited with status "),
WEXITSTATUS(status));
g_spawn_close_pid(terminal->tabs[i]->pid);
terminal->tabs[i]->pid = -1;
_terminal_close_tab(terminal, i);
}
else if(WIFSIGNALED(status))
{
fprintf(stderr, "%s: %s%u\n", PROGNAME,
_("xterm exited with signal "),
WTERMSIG(status));
g_spawn_close_pid(terminal->tabs[i]->pid);
terminal->tabs[i]->pid = -1;
_terminal_close_tab(terminal, i);
}
}
/* terminal_on_close */
static void _terminal_on_close(gpointer data)
{
Terminal * terminal = data;
int i;
i = gtk_notebook_get_current_page(GTK_NOTEBOOK(terminal->notebook));
if(i < 0)
return;
_terminal_close_tab(terminal, i);
}
/* terminal_on_closex */
static gboolean _on_closex_confirm(Terminal * terminal);
static gboolean _terminal_on_closex(gpointer data)
{
Terminal * terminal = data;
if(terminal->tabs_cnt > 1 && _on_closex_confirm(terminal) != TRUE)
return TRUE;
_terminal_close_all(terminal);
return TRUE;
}
static gboolean _on_closex_confirm(Terminal * terminal)
{
GtkWidget * dialog;
gboolean res;
dialog = gtk_message_dialog_new(GTK_WINDOW(terminal->window),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
#if GTK_CHECK_VERSION(2, 6, 0)
"%s", _("Question"));
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
#endif
"%s", _("There are multiple tabs opened.\n"
"Do you really want to close every tab"
" opened in this window?"));
gtk_dialog_add_buttons(GTK_DIALOG(dialog),
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
res = gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
return (res == GTK_RESPONSE_CLOSE) ? TRUE : FALSE;
}
/* terminal_on_fullscreen */
static void _terminal_on_fullscreen(gpointer data)
{
Terminal * terminal = data;
terminal_set_fullscreen(terminal, terminal->fullscreen ? FALSE : TRUE);
}
/* terminal_on_new_tab */
static void _terminal_on_new_tab(gpointer data)
{
Terminal * terminal = data;
_terminal_open_tab(terminal);
}
/* terminal_on_new_window */
static void _terminal_on_new_window(gpointer data)
{
Terminal * terminal = data;
_terminal_open_window(terminal);
}
/* terminal_on_tab_close */
static void _terminal_on_tab_close(gpointer data)
{
TerminalTab * tab = data;
size_t i;
for(i = 0; i < tab->terminal->tabs_cnt; i++)
if(tab->terminal->tabs[i]->widget == tab->widget)
break;
if(i >= tab->terminal->tabs_cnt)
/* should not happen */
return;
_terminal_close_tab(tab->terminal, i);
}
/* terminal_on_tab_rename */
static void _terminal_on_tab_rename(gpointer data)
{
TerminalTab * tab = data;
GtkWidget * dialog;
GtkWidget * content;
GtkWidget * entry;
gchar const * p;
dialog = gtk_message_dialog_new(GTK_WINDOW(tab->terminal->window),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_OTHER, GTK_BUTTONS_NONE,
#if GTK_CHECK_VERSION(2, 6, 0)
"%s", _("Rename tab"));
# if GTK_CHECK_VERSION(2, 10, 0)
gtk_message_dialog_set_image(GTK_MESSAGE_DIALOG(dialog),
gtk_image_new_from_stock(GTK_STOCK_EDIT,
GTK_ICON_SIZE_DIALOG));
# endif
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
#endif
"%s", _("Rename this tab as:"));
gtk_dialog_add_buttons(GTK_DIALOG(dialog),
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
gtk_window_set_title(GTK_WINDOW(dialog), _("Rename tab"));
#if GTK_CHECK_VERSION(2, 14, 0)
content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
#else
content = GTK_DIALOG(dialog)->vbox;
#endif
entry = gtk_entry_new();
gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
gtk_entry_set_text(GTK_ENTRY(entry), gtk_label_get_text(
GTK_LABEL(tab->label)));
gtk_box_pack_start(GTK_BOX(content), entry, FALSE, TRUE, 0);
gtk_widget_show_all(content);
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
{
p = gtk_entry_get_text(GTK_ENTRY(entry));
gtk_label_set_text(GTK_LABEL(tab->label), p);
}
gtk_widget_destroy(dialog);
}
#ifndef EMBEDDED
/* terminal_on_file_close */
static void _terminal_on_file_close(gpointer data)
{
Terminal * terminal = data;
_terminal_on_close(terminal);
}
/* terminal_on_file_close_all */
static void _terminal_on_file_close_all(gpointer data)
{
Terminal * terminal = data;
_terminal_close_all(terminal);
}
/* terminal_on_file_new_tab */
static void _terminal_on_file_new_tab(gpointer data)
{
Terminal * terminal = data;
_terminal_open_tab(terminal);
}
/* terminal_on_file_new_window */
static void _terminal_on_file_new_window(gpointer data)
{
Terminal * terminal = data;
_terminal_open_window(terminal);
}
/* terminal_on_help_about */
static void _terminal_on_help_about(gpointer data)
{
Terminal * terminal = data;
GtkWidget * dialog;
dialog = desktop_about_dialog_new();
gtk_window_set_transient_for(GTK_WINDOW(dialog),
GTK_WINDOW(terminal->window));
desktop_about_dialog_set_authors(dialog, _authors);
desktop_about_dialog_set_comments(dialog,
_("Terminal for the DeforaOS desktop"));
desktop_about_dialog_set_copyright(dialog, _copyright);
desktop_about_dialog_set_license(dialog, _license);
desktop_about_dialog_set_logo_icon_name(dialog, "terminal");
desktop_about_dialog_set_program_name(dialog, PACKAGE);
desktop_about_dialog_set_version(dialog, VERSION);
desktop_about_dialog_set_website(dialog, "http://www.defora.org/");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
/* terminal_on_help_contents */
static void _terminal_on_help_contents(gpointer data)
{
desktop_help_contents(PACKAGE, PROGNAME);
}
/* terminal_on_view_fullscreen */
static void _terminal_on_view_fullscreen(gpointer data)
{
Terminal * terminal = data;
_terminal_on_fullscreen(terminal);
}
#endif

View File

@ -1,5 +1,5 @@
/* $Id$ */
/* Copyright (c) 2012-2016 Pierre Pronchery <khorben@defora.org> */
/* Copyright (c) 2012-2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Terminal */
/* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -12,10 +12,6 @@
* notice this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the authors nor the names of the contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
@ -33,27 +29,36 @@
#ifndef TERMINAL_TERMINAL_H
# define TERMINAL_TERMINAL_H
# include "common.h"
/* Terminal */
/* public */
/* types */
typedef struct _TerminalPrefs
{
char const * shell;
char const * directory;
String const * shell;
String const * directory;
unsigned int login;
} TerminalPrefs;
typedef struct _Terminal Terminal;
/* functions */
/* essential */
Terminal * terminal_new(TerminalPrefs * prefs);
void terminal_delete(Terminal * terminal);
/* accessors */
void terminal_set_fullscreen(Terminal * terminal, gboolean fullscreen);
String const * terminal_get_directory(Terminal * terminal);
String const * terminal_get_shell(Terminal * terminal);
TerminalWindow * terminal_get_window(Terminal * terminal);
gboolean terminal_is_login(Terminal * terminal);
/* useful */
void terminal_tab_close(Terminal * terminal, TerminalTab * tab);
void terminal_tab_close_all(Terminal * terminal);
TerminalTab * terminal_tab_open(Terminal * terminal);
int terminal_window_open(Terminal * terminal);
#endif /* !TERMINAL_TERMINAL_H */

581
src/widget.c Normal file
View File

@ -0,0 +1,581 @@
/* $Id$ */
static char const _copyright[] =
"Copyright © 2012-2018 Pierre Pronchery <khorben@defora.org>";
/* This file is part of DeforaOS Desktop Terminal */
static char const _license[] =
"Redistribution and use in source and binary forms, with or without\n"
"modification, are permitted provided that the following conditions are\n"
"met:\n"
"\n"
"1. Redistributions of source code must retain the above copyright\n"
" notice, this list of conditions and the following disclaimer.\n"
"\n"
"2. Redistributions in binary form must reproduce the above copyright\n"
" notice this list of conditions and the following disclaimer in the\n"
" documentation and/or other materials provided with the distribution.\n"
"\n"
"THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS \"AS IS\" AND\n"
"ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n"
"PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS\n"
"BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
"CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
"SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
"INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n"
"CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n"
"ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n"
"THE POSSIBILITY OF SUCH DAMAGE.";
#include <libintl.h>
#include <gdk/gdkkeysyms.h>
#include <System.h>
#include <Desktop.h>
#include "common.h"
#include "tab.h"
#include "terminal.h"
#include "window.h"
#include "widget.h"
#include "../config.h"
#define _(string) gettext(string)
#define N_(string) (string)
/* constants */
#ifndef PROGNAME_TERMINAL
# define PROGNAME_TERMINAL "terminal"
#endif
/* TerminalWidget */
/* private */
/* types */
struct _TerminalWidget
{
Terminal * terminal;
TerminalTab ** tabs;
size_t tabs_cnt;
/* widgets */
GtkWidget * widget;
#ifndef EMBEDDED
GtkWidget * menubar;
#endif
GtkWidget * about;
GtkToolItem * fullscreen;
GtkWidget * notebook;
};
/* prototypes */
static void _terminalwidget_tab_close(TerminalWidget * widget, unsigned int i);
/* callbacks */
static void _terminalwidget_on_close(gpointer data);
static void _terminalwidget_on_copy(gpointer data);
static void _terminalwidget_on_fullscreen(gpointer data);
static void _terminalwidget_on_new_tab(gpointer data);
static void _terminalwidget_on_new_window(gpointer data);
static void _terminalwidget_on_paste(gpointer data);
#ifndef EMBEDDED
static void _terminalwidget_on_file_close(gpointer data);
static void _terminalwidget_on_file_close_all(gpointer data);
static void _terminalwidget_on_file_new_tab(gpointer data);
static void _terminalwidget_on_file_new_window(gpointer data);
static void _terminalwidget_on_edit_copy(gpointer data);
static void _terminalwidget_on_edit_paste(gpointer data);
static void _terminalwidget_on_view_fullscreen(gpointer data);
static void _terminalwidget_on_help_about(gpointer data);
static void _terminalwidget_on_help_contents(gpointer data);
#endif
/* constants */
static char const * _authors[] =
{
"Pierre Pronchery <khorben@defora.org>",
NULL
};
#ifndef EMBEDDED
/* menubar */
static const DesktopMenu _terminalwidget_file_menu[] =
{
{ N_("New _tab"), G_CALLBACK(_terminalwidget_on_file_new_tab),
"tab-new", GDK_CONTROL_MASK, GDK_KEY_T },
{ N_("_New window"), G_CALLBACK(_terminalwidget_on_file_new_window),
"window-new", GDK_CONTROL_MASK, GDK_KEY_N },
{ "", NULL, NULL, 0, 0 },
{ N_("_Close"), G_CALLBACK(_terminalwidget_on_file_close),
GTK_STOCK_CLOSE, GDK_CONTROL_MASK, GDK_KEY_W },
{ N_("Close all tabs"), G_CALLBACK(_terminalwidget_on_file_close_all),
NULL, GDK_SHIFT_MASK | GDK_CONTROL_MASK, GDK_KEY_W },
{ NULL, NULL, NULL, 0, 0 }
};
static const DesktopMenu _terminalwidget_edit_menu[] =
{
{ N_("_Copy"), G_CALLBACK(_terminalwidget_on_edit_copy),
GTK_STOCK_COPY, GDK_SHIFT_MASK | GDK_CONTROL_MASK, GDK_KEY_C },
{ N_("_Paste"), G_CALLBACK(_terminalwidget_on_edit_paste),
GTK_STOCK_PASTE, GDK_SHIFT_MASK | GDK_CONTROL_MASK, GDK_KEY_V },
{ NULL, NULL, NULL, 0, 0 }
};
static const DesktopMenu _terminalwidget_view_menu[] =
{
{ N_("_Fullscreen"), G_CALLBACK(_terminalwidget_on_view_fullscreen),
# if GTK_CHECK_VERSION(2, 8, 0)
GTK_STOCK_FULLSCREEN,
# else
NULL,
# endif
0, GDK_KEY_F11 },
{ NULL, NULL, NULL, 0, 0 }
};
static const DesktopMenu _terminalwidget_help_menu[] =
{
{ N_("_Contents"), G_CALLBACK(_terminalwidget_on_help_contents),
"help-contents", 0, GDK_KEY_F1 },
#if GTK_CHECK_VERSION(2, 6, 0)
{ N_("_About"), G_CALLBACK(_terminalwidget_on_help_about),
GTK_STOCK_ABOUT, 0, 0 },
#else
{ N_("_About"), G_CALLBACK(_terminalwidget_on_help_about), NULL, 0, 0 },
#endif
{ NULL, NULL, NULL, 0, 0 }
};
static const DesktopMenubar _terminalwidget_menubar[] =
{
{ N_("_File"), _terminalwidget_file_menu },
{ N_("_Edit"), _terminalwidget_edit_menu },
{ N_("_View"), _terminalwidget_view_menu },
{ N_("_Help"), _terminalwidget_help_menu },
{ NULL, NULL }
};
#endif
static DesktopToolbar _terminalwidget_toolbar[] =
{
{ N_("New tab"), G_CALLBACK(_terminalwidget_on_new_tab), "tab-new", 0,
0, NULL },
{ N_("New window"), G_CALLBACK(_terminalwidget_on_new_window),
"window-new", 0, 0, NULL },
{ "", NULL, NULL, 0, 0, NULL },
{ N_("Copy"), G_CALLBACK(_terminalwidget_on_copy), GTK_STOCK_COPY, 0,
0, NULL },
{ N_("Paste"), G_CALLBACK(_terminalwidget_on_paste), GTK_STOCK_PASTE, 0,
0, NULL },
{ "", NULL, NULL, 0, 0, NULL },
{ NULL, NULL, NULL, 0, 0, NULL }
};
/* public */
/* functions */
/* terminalwidget_new */
TerminalWidget * terminalwidget_new(Terminal * terminal, GtkWidget * window)
{
TerminalWidget * widget;
GtkAccelGroup * group;
GtkWidget * vbox;
GtkWidget * toolbar;
GtkToolItem * toolitem;
if((widget = object_new(sizeof(*widget))) == NULL)
return NULL;
widget->terminal = terminal;
widget->tabs = NULL;
widget->tabs_cnt = 0;
group = gtk_accel_group_new();
widget->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
vbox = widget->widget;
#ifndef EMBEDDED
/* menubar */
widget->menubar = desktop_menubar_create(_terminalwidget_menubar,
widget, group);
gtk_box_pack_start(GTK_BOX(vbox), widget->menubar, FALSE, TRUE, 0);
#endif
widget->about = NULL;
/* toolbar */
toolbar = desktop_toolbar_create(_terminalwidget_toolbar, widget, group);
#if GTK_CHECK_VERSION(2, 8, 0)
toolitem = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_FULLSCREEN);
#else
toolitem = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_ZOOM_FIT);
#endif
widget->fullscreen = toolitem;
g_signal_connect_swapped(G_OBJECT(toolitem), "toggled", G_CALLBACK(
_terminalwidget_on_fullscreen), widget);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, TRUE, 0);
gtk_window_add_accel_group(GTK_WINDOW(window), group);
g_object_unref(group);
/* view */
widget->notebook = gtk_notebook_new();
gtk_notebook_set_scrollable(GTK_NOTEBOOK(widget->notebook), TRUE);
gtk_box_pack_start(GTK_BOX(vbox), widget->notebook, TRUE, TRUE, 0);
gtk_widget_show_all(vbox);
return widget;
}
/* terminalwidget_delete */
void terminalwidget_delete(TerminalWidget * widget)
{
size_t i;
for(i = 0; i < widget->tabs_cnt; i++)
terminaltab_delete(widget->tabs[i]);
/* FIXME also take care of the sub-processes */
free(widget->tabs);
object_delete(widget);
}
/* accessors */
/* terminalwidget_get_fullscreen */
gboolean terminalwidget_get_fullscreen(TerminalWidget * widget)
{
TerminalWindow * window;
window = terminal_get_window(widget->terminal);
return terminalwindow_get_fullscreen(window);
}
/* terminalwidget_get_widget */
GtkWidget * terminalwidget_get_widget(TerminalWidget * widget)
{
return widget->widget;
}
/* terminalwidget_set_fullscreen */
void terminalwidget_set_fullscreen(TerminalWidget * widget,
gboolean fullscreen)
{
TerminalWindow * window;
#ifndef EMBEDDED
if(fullscreen)
gtk_widget_hide(widget->menubar);
else
gtk_widget_show(widget->menubar);
#endif
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(
widget->fullscreen), fullscreen);
window = terminal_get_window(widget->terminal);
terminalwindow_set_fullscreen(window, fullscreen);
}
/* useful */
/* interface */
/* terminalwidget_show_about */
static gboolean _show_about_on_closex(gpointer data);
void terminalwidget_show_about(TerminalWidget * widget, gboolean show)
{
TerminalWindow * twindow;
GtkWidget * window;
if(widget->about == NULL)
{
twindow = terminal_get_window(widget->terminal);
window = terminalwindow_get_window(twindow);
widget->about = desktop_about_dialog_new();
gtk_window_set_modal(GTK_WINDOW(widget->about), TRUE);
gtk_window_set_transient_for(GTK_WINDOW(widget->about),
GTK_WINDOW(window));
desktop_about_dialog_set_authors(widget->about, _authors);
desktop_about_dialog_set_comments(widget->about,
_("Terminal for the DeforaOS desktop"));
desktop_about_dialog_set_copyright(widget->about, _copyright);
desktop_about_dialog_set_license(widget->about, _license);
desktop_about_dialog_set_logo_icon_name(widget->about,
"terminal");
desktop_about_dialog_set_program_name(widget->about, PACKAGE);
desktop_about_dialog_set_version(widget->about, VERSION);
desktop_about_dialog_set_website(widget->about,
"https://www.defora.org/");
g_signal_connect_swapped(widget->about, "delete-event",
G_CALLBACK(_show_about_on_closex), widget);
}
if(show)
gtk_widget_show(widget->about);
else
gtk_widget_hide(widget->about);
}
static gboolean _show_about_on_closex(gpointer data)
{
TerminalWidget * widget = data;
gtk_widget_hide(widget->about);
return TRUE;
}
/* terminalwidget_show_preferences */
void terminalwidget_show_preferences(TerminalWidget * terminal, gboolean show)
{
/* FIXME implement */
}
/* terminalwidget_tab_close */
void terminalwidget_tab_close(TerminalWidget * widget, TerminalTab * tab)
{
size_t i;
for(i = 0; i < widget->tabs_cnt; i++)
if(widget->tabs[i] == tab)
{
_terminalwidget_tab_close(widget, i);
return;
}
}
/* terminalwidget_tab_close_all */
static gboolean _tab_close_all_confirm(TerminalWidget * widget);
void terminalwidget_tab_close_all(TerminalWidget * widget)
{
if(widget->tabs_cnt > 1 && _tab_close_all_confirm(widget) != TRUE)
return;
while(widget->tabs_cnt > 0)
_terminalwidget_tab_close(widget, widget->tabs_cnt - 1);
}
static gboolean _tab_close_all_confirm(TerminalWidget * widget)
{
TerminalWindow * twindow;
GtkWidget * window;
GtkWidget * dialog;
gboolean res;
twindow = terminal_get_window(widget->terminal);
window = terminalwindow_get_window(twindow);
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
#if GTK_CHECK_VERSION(2, 6, 0)
"%s", _("Question"));
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
#endif
"%s", _("There are multiple tabs opened.\n"
"Do you really want to close every tab"
" opened in this window?"));
gtk_dialog_add_buttons(GTK_DIALOG(dialog),
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
res = gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
return (res == GTK_RESPONSE_CLOSE) ? TRUE : FALSE;
}
/* terminalwidget_tab_open */
TerminalTab * terminalwidget_tab_open(TerminalWidget * widget)
{
TerminalTab ** p;
TerminalTab * tab;
GtkWidget * label;
GtkWidget * child;
if((p = realloc(widget->tabs, sizeof(*p) * (widget->tabs_cnt + 1)))
== NULL)
return NULL;
widget->tabs = p;
if((tab = terminaltab_new(widget->terminal)) == NULL)
return NULL;
widget->tabs[widget->tabs_cnt++] = tab;
label = terminaltab_get_label(tab);
child = terminaltab_get_widget(tab);
gtk_notebook_append_page(GTK_NOTEBOOK(widget->notebook), child, label);
#if GTK_CHECK_VERSION(2, 10, 0)
gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(widget->notebook), child,
TRUE);
#endif
return tab;
}
/* private */
/* terminalwidget_tab_close */
static void _terminalwidget_tab_close(TerminalWidget * widget, unsigned int i)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%u)\n", __func__, i);
#endif
if(i >= widget->tabs_cnt)
return;
terminaltab_delete(widget->tabs[i]);
gtk_notebook_remove_page(GTK_NOTEBOOK(widget->notebook), i);
memmove(&widget->tabs[i], &widget->tabs[i + 1],
(widget->tabs_cnt - (i + 1)) * sizeof(*widget->tabs));
if(--widget->tabs_cnt == 0)
/* XXX there might be multiple windows opened */
gtk_main_quit();
}
/* callbacks */
/* terminalwidget_on_close */
static void _terminalwidget_on_close(gpointer data)
{
TerminalWidget * widget = data;
int i;
i = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget->notebook));
if(i < 0)
return;
_terminalwidget_tab_close(widget, i);
}
/* terminalwidget_on_copy */
static void _terminalwidget_on_copy(gpointer data)
{
TerminalWidget * widget = data;
int i;
i = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget->notebook));
if(i < 0)
return;
terminaltab_clipboard_copy(widget->tabs[i]);
}
/* terminalwidget_on_fullscreen */
static void _terminalwidget_on_fullscreen(gpointer data)
{
TerminalWidget * widget = data;
TerminalWindow * window;
window = terminal_get_window(widget->terminal);
terminalwindow_set_fullscreen(window,
terminalwindow_get_fullscreen(window) ? FALSE : TRUE);
}
/* terminalwidget_on_new_tab */
static void _terminalwidget_on_new_tab(gpointer data)
{
TerminalWidget * widget = data;
terminalwidget_tab_open(widget);
}
/* terminalwidget_on_new_window */
static void _terminalwidget_on_new_window(gpointer data)
{
TerminalWidget * widget = data;
/* XXX may fail */
terminal_window_open(widget->terminal);
}
/* terminalwidget_on_paste */
static void _terminalwidget_on_paste(gpointer data)
{
TerminalWidget * widget = data;
int i;
i = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget->notebook));
if(i < 0)
return;
terminaltab_clipboard_paste(widget->tabs[i]);
}
#ifndef EMBEDDED
/* terminalwidget_on_edit_copy */
static void _terminalwidget_on_edit_copy(gpointer data)
{
TerminalWidget * widget = data;
_terminalwidget_on_copy(widget);
}
/* terminalwidget_on_edit_paste */
static void _terminalwidget_on_edit_paste(gpointer data)
{
TerminalWidget * widget = data;
_terminalwidget_on_paste(widget);
}
/* terminalwidget_on_file_close */
static void _terminalwidget_on_file_close(gpointer data)
{
TerminalWidget * widget = data;
_terminalwidget_on_close(widget);
}
/* terminalwidget_on_file_close_all */
static void _terminalwidget_on_file_close_all(gpointer data)
{
TerminalWidget * widget = data;
terminalwidget_tab_close_all(widget);
}
/* terminalwidget_on_file_new_tab */
static void _terminalwidget_on_file_new_tab(gpointer data)
{
TerminalWidget * widget = data;
terminalwidget_tab_open(widget);
}
/* terminalwidget_on_file_new_window */
static void _terminalwidget_on_file_new_window(gpointer data)
{
TerminalWidget * widget = data;
terminal_window_open(widget->terminal);
}
/* terminalwidget_on_help_about */
static void _terminalwidget_on_help_about(gpointer data)
{
TerminalWidget * widget = data;
terminalwidget_show_about(widget, TRUE);
}
/* terminalwidget_on_help_contents */
static void _terminalwidget_on_help_contents(gpointer data)
{
(void) data;
desktop_help_contents(PACKAGE, PROGNAME_TERMINAL);
}
/* terminalwidget_on_view_fullscreen */
static void _terminalwidget_on_view_fullscreen(gpointer data)
{
TerminalWidget * widget = data;
_terminalwidget_on_fullscreen(widget);
}
#endif

59
src/widget.h Normal file
View File

@ -0,0 +1,59 @@
/* $Id$ */
/* Copyright (c) 2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Terminal */
/* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. */
#ifndef TERMINAL_WIDGET_H
# define TERMINAL_WIDGET_H
# include "common.h"
/* TerminalWidget */
/* public */
/* functions */
TerminalWidget * terminalwidget_new(Terminal * terminal, GtkWidget * window);
void terminalwidget_delete(TerminalWidget * widget);
/* accessors */
gboolean terminalwidget_get_fullscreen(TerminalWidget * widget);
void terminalwidget_set_fullscreen(TerminalWidget * widget,
gboolean fullscreen);
GtkWidget * terminalwidget_get_widget(TerminalWidget * widget);
/* useful */
/* interface */
void terminalwidget_show_about(TerminalWidget * widget, gboolean show);
void terminalwidget_show_preferences(TerminalWidget * widget, gboolean show);
void terminalwidget_tab_close(TerminalWidget * widget, TerminalTab * tab);
void terminalwidget_tab_close_all(TerminalWidget * widget);
TerminalTab * terminalwidget_tab_open(TerminalWidget * widget);
#endif /* !TERMINAL_WIDGET_H */

159
src/window.c Normal file
View File

@ -0,0 +1,159 @@
/* $Id$ */
/* Copyright (c) 2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Terminal */
/* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. */
#include <libintl.h>
#include <System.h>
#include "widget.h"
#include "window.h"
#define _(string) gettext(string)
/* TerminalWindow */
/* private */
/* types */
struct _TerminalWindow
{
Terminal * terminal;
TerminalWidget * widget;
gboolean fullscreen;
/* widgets */
GtkWidget * window;
};
/* prototypes */
/* callbacks */
static gboolean _terminalwindow_on_closex(gpointer data);
/* public */
/* functions */
/* terminalwindow_new */
TerminalWindow * terminalwindow_new(Terminal * terminal)
{
TerminalWindow * window;
GtkWidget * widget;
if((window = object_new(sizeof(*window))) == NULL)
return NULL;
window->terminal = terminal;
window->fullscreen = FALSE;
window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
if((window->widget = terminalwidget_new(terminal, window->window))
== NULL)
{
terminalwindow_delete(window);
return NULL;
}
/* XXX really default to 80x24 (or a terminal size) */
gtk_window_set_default_size(GTK_WINDOW(window->window), 600, 400);
#if GTK_CHECK_VERSION(2, 6, 0)
gtk_window_set_icon_name(GTK_WINDOW(window->window), "terminal");
#endif
gtk_window_set_title(GTK_WINDOW(window->window), _("Terminal"));
g_signal_connect_swapped(window->window, "delete-event", G_CALLBACK(
_terminalwindow_on_closex), window);
widget = terminalwidget_get_widget(window->widget);
gtk_container_add(GTK_CONTAINER(window->window), widget);
gtk_widget_show(window->window);
return window;
}
/* terminalwindow_delete */
void terminalwindow_delete(TerminalWindow * window)
{
if(window->widget != NULL)
terminalwidget_delete(window->widget);
gtk_widget_destroy(window->window);
object_delete(window);
}
/* accessors */
/* terminalwindow_get_fullscreen */
gboolean terminalwindow_get_fullscreen(TerminalWindow * window)
{
return window->fullscreen;
}
/* terminalwindow_get_widget */
TerminalWidget * terminalwindow_get_widget(TerminalWindow * window)
{
return window->widget;
}
/* terminalwindow_get_widget */
GtkWidget * terminalwindow_get_window(TerminalWindow * window)
{
return window->window;
}
/* terminalwindow_set_fullscreen */
void terminalwindow_set_fullscreen(TerminalWindow * window, gboolean fullscreen)
{
if(fullscreen == window->fullscreen)
return;
window->fullscreen = fullscreen;
terminalwidget_set_fullscreen(window->widget, fullscreen);
if(fullscreen)
gtk_window_fullscreen(GTK_WINDOW(window->window));
else
gtk_window_unfullscreen(GTK_WINDOW(window->window));
}
/* useful */
/* terminalwindow_show_about */
void terminalwindow_show_about(TerminalWindow * terminal, gboolean show)
{
terminalwidget_show_about(terminal->widget, show);
}
/* terminalwindow_show_preferences */
void terminalwindow_show_preferences(TerminalWindow * terminal, gboolean show)
{
terminalwidget_show_preferences(terminal->widget, show);
}
/* private */
/* callbacks */
/* terminalwindow_on_closex */
static gboolean _terminalwindow_on_closex(gpointer data)
{
TerminalWindow * window = data;
terminalwidget_tab_close_all(window->widget);
return TRUE;
}

55
src/window.h Normal file
View File

@ -0,0 +1,55 @@
/* $Id$ */
/* Copyright (c) 2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Terminal */
/* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE. */
#ifndef TERMINAL_WINDOW_H
# define TERMINAL_WINDOW_H
# include "common.h"
/* TerminalWindow */
/* public */
/* functions */
TerminalWindow * terminalwindow_new(Terminal * terminal);
void terminalwindow_delete(TerminalWindow * window);
/* accessors */
gboolean terminalwindow_get_fullscreen(TerminalWindow * window);
void terminalwindow_set_fullscreen(TerminalWindow * window,
gboolean fullscreen);
TerminalWidget * terminalwindow_get_widget(TerminalWindow * window);
GtkWidget * terminalwindow_get_window(TerminalWindow * window);
/* useful */
/* interface */
void terminalwindow_show_about(TerminalWindow * terminal, gboolean show);
void terminalwindow_show_preferences(TerminalWindow * terminal, gboolean show);
#endif /* !TERMINAL_WINDOW_H */