зеркало из https://github.com/github/putty.git
Divide the whole of gtkwin.c into three parts.
This lays further groundwork for the OS X GTK3 port, which is going to have to deal with multiple sessions sharing the same process. gtkwin.c was a bit too monolithic for this, since it included some process-global runtime state (timers, toplevel callbacks), some process startup stuff (gtk_init, gtk_main, argv processing) and some per-session-window stuff. The per-session stuff remains in gtkwin.c, with the top-level function now being new_session_window() taking a Conf. The new gtkmain.c contains the outer skeleton of pt_main(), handling argv processing and one-off startup stuff like setlocale; and the new gtkcomm.c contains the pieces of PuTTY infrastructure like timers and uxsel that are shared between multiple sessions rather than reinstantiated per session, which have been rewritten to use global variables rather than fields in 'inst' (since it's now clear to me that they'll have to apply to all the insts in existence at once). There are still some lurking assumptions of one-session-per-process, e.g. the use of gtk_main_quit when a session finishes, and the fact that the config box insists on running as a separate invocation of gtk_main so that one session's preliminary config box can't coexist with another session already active. But this should make it possible to at least write an OS X app good enough to start testing with, even if it doesn't get everything quite right yet. This change is almost entirely rearranging existing code, so it shouldn't be seriously destabilising. But two noticeable actual changes have happened, both pleasantly simplifying: Firstly, the global-variables rewrite of gtkcomm.c has allowed the post_main edifice to become a great deal simpler. Most of its complexity was about remembering what 'inst' it had to call back to, and in fact the right answer is that it shouldn't be calling back to one at all. So now the post_main() called by gtkdlg.c has become the same function as the old inst_post_main() that actually did the work, instead of the two having to be connected by a piece of ugly plumbing. Secondly, a piece of code that's vanished completely in this refactoring is the temporary blocking of SIGCHLD around most of the session setup code. This turns out to have been introduced in 2002, _before_ I switched to using the intra-process signal pipe strategy for SIGCHLD handling in 2003. So I now expect that we should be robust in any case against receiving SIGCHLD at an inconvenient moment, and hence there's no need to block it.
This commit is contained in:
Родитель
a5588cd635
Коммит
eac66b0281
9
Recipe
9
Recipe
|
@ -217,7 +217,8 @@ GUITERM = TERMINAL window windlg winctrls sizetip winucs winprint
|
|||
|
||||
# Same thing on Unix.
|
||||
UXTERM = TERMINAL uxcfg sercfg uxucs uxprint timing callback miscucs
|
||||
GTKTERM = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols gtkmisc xkeysym x11misc
|
||||
GTKTERM = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols gtkmisc xkeysym
|
||||
+ x11misc gtkcomm
|
||||
OSXTERM = UXTERM osxwin osxdlg osxctrls
|
||||
|
||||
# Non-SSH back ends (putty, puttytel, plink).
|
||||
|
@ -294,13 +295,13 @@ puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
|
|||
|
||||
pterm : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore
|
||||
+ uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg
|
||||
+ nogss
|
||||
+ nogss gtkmain
|
||||
putty : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_ALL uxstore
|
||||
+ uxsignal CHARSET uxputty NONSSH UXSSH UXMISC ux_x11 xpmputty
|
||||
+ xpmpucfg
|
||||
+ xpmpucfg gtkmain
|
||||
puttytel : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_NOSSH
|
||||
+ uxstore uxsignal CHARSET uxputty NONSSH UXMISC xpmputty xpmpucfg
|
||||
+ nogss
|
||||
+ nogss gtkmain
|
||||
|
||||
plink : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal
|
||||
+ ux_x11 noterm
|
||||
|
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* gtkcomm.c: machinery in the GTK front end which is common to all
|
||||
* programs that run a session in a terminal window, and also common
|
||||
* across all _sessions_ rather than specific to one session. (Timers,
|
||||
* uxsel etc.)
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <gtk/gtk.h>
|
||||
#if !GTK_CHECK_VERSION(3,0,0)
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#endif
|
||||
|
||||
#if GTK_CHECK_VERSION(2,0,0)
|
||||
#include <gtk/gtkimmodule.h>
|
||||
#endif
|
||||
|
||||
#define MAY_REFER_TO_GTK_IN_HEADERS
|
||||
|
||||
#include "putty.h"
|
||||
#include "terminal.h"
|
||||
#include "gtkcompat.h"
|
||||
#include "gtkfont.h"
|
||||
#include "gtkmisc.h"
|
||||
|
||||
#ifndef NOT_X_WINDOWS
|
||||
#include <gdk/gdkx.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#endif
|
||||
|
||||
#define CAT2(x,y) x ## y
|
||||
#define CAT(x,y) CAT2(x,y)
|
||||
#define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)}
|
||||
|
||||
#if GTK_CHECK_VERSION(2,0,0)
|
||||
ASSERT(sizeof(long) <= sizeof(gsize));
|
||||
#define LONG_TO_GPOINTER(l) GSIZE_TO_POINTER(l)
|
||||
#define GPOINTER_TO_LONG(p) GPOINTER_TO_SIZE(p)
|
||||
#else /* Gtk 1.2 */
|
||||
ASSERT(sizeof(long) <= sizeof(gpointer));
|
||||
#define LONG_TO_GPOINTER(l) ((gpointer)(long)(l))
|
||||
#define GPOINTER_TO_LONG(p) ((long)(p))
|
||||
#endif
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* File descriptors and uxsel.
|
||||
*/
|
||||
|
||||
struct uxsel_id {
|
||||
#if GTK_CHECK_VERSION(2,0,0)
|
||||
GIOChannel *chan;
|
||||
guint watch_id;
|
||||
#else
|
||||
int id;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if GTK_CHECK_VERSION(2,0,0)
|
||||
gboolean fd_input_func(GIOChannel *source, GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
int sourcefd = g_io_channel_unix_get_fd(source);
|
||||
/*
|
||||
* We must process exceptional notifications before ordinary
|
||||
* readability ones, or we may go straight past the urgent
|
||||
* marker.
|
||||
*/
|
||||
if (condition & G_IO_PRI)
|
||||
select_result(sourcefd, 4);
|
||||
if (condition & G_IO_IN)
|
||||
select_result(sourcefd, 1);
|
||||
if (condition & G_IO_OUT)
|
||||
select_result(sourcefd, 2);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#else
|
||||
void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
|
||||
{
|
||||
if (condition & GDK_INPUT_EXCEPTION)
|
||||
select_result(sourcefd, 4);
|
||||
if (condition & GDK_INPUT_READ)
|
||||
select_result(sourcefd, 1);
|
||||
if (condition & GDK_INPUT_WRITE)
|
||||
select_result(sourcefd, 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
uxsel_id *uxsel_input_add(int fd, int rwx) {
|
||||
uxsel_id *id = snew(uxsel_id);
|
||||
|
||||
#if GTK_CHECK_VERSION(2,0,0)
|
||||
int flags = 0;
|
||||
if (rwx & 1) flags |= G_IO_IN;
|
||||
if (rwx & 2) flags |= G_IO_OUT;
|
||||
if (rwx & 4) flags |= G_IO_PRI;
|
||||
id->chan = g_io_channel_unix_new(fd);
|
||||
g_io_channel_set_encoding(id->chan, NULL, NULL);
|
||||
id->watch_id = g_io_add_watch_full(id->chan, GDK_PRIORITY_REDRAW+1, flags,
|
||||
fd_input_func, NULL, NULL);
|
||||
#else
|
||||
int flags = 0;
|
||||
if (rwx & 1) flags |= GDK_INPUT_READ;
|
||||
if (rwx & 2) flags |= GDK_INPUT_WRITE;
|
||||
if (rwx & 4) flags |= GDK_INPUT_EXCEPTION;
|
||||
assert(flags);
|
||||
id->id = gdk_input_add(fd, flags, fd_input_func, NULL);
|
||||
#endif
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void uxsel_input_remove(uxsel_id *id) {
|
||||
#if GTK_CHECK_VERSION(2,0,0)
|
||||
g_source_remove(id->watch_id);
|
||||
g_io_channel_unref(id->chan);
|
||||
#else
|
||||
gdk_input_remove(id->id);
|
||||
#endif
|
||||
sfree(id);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Timers.
|
||||
*/
|
||||
|
||||
static guint timer_id = 0;
|
||||
|
||||
static gint timer_trigger(gpointer data)
|
||||
{
|
||||
unsigned long now = GPOINTER_TO_LONG(data);
|
||||
unsigned long next, then;
|
||||
long ticks;
|
||||
|
||||
/*
|
||||
* Destroy the timer we got here on.
|
||||
*/
|
||||
if (timer_id) {
|
||||
g_source_remove(timer_id);
|
||||
timer_id = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* run_timers() may cause a call to timer_change_notify, in which
|
||||
* case a new timer will already have been set up and left in
|
||||
* timer_id. If it hasn't, and run_timers reports that some timing
|
||||
* still needs to be done, we do it ourselves.
|
||||
*/
|
||||
if (run_timers(now, &next) && !timer_id) {
|
||||
then = now;
|
||||
now = GETTICKCOUNT();
|
||||
if (now - then > next - then)
|
||||
ticks = 0;
|
||||
else
|
||||
ticks = next - now;
|
||||
timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returning FALSE means 'don't call this timer again', which
|
||||
* _should_ be redundant given that we removed it above, but just
|
||||
* in case, return FALSE anyway.
|
||||
*/
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void timer_change_notify(unsigned long next)
|
||||
{
|
||||
long ticks;
|
||||
|
||||
if (timer_id)
|
||||
g_source_remove(timer_id);
|
||||
|
||||
ticks = next - GETTICKCOUNT();
|
||||
if (ticks <= 0)
|
||||
ticks = 1; /* just in case */
|
||||
|
||||
timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next));
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Toplevel callbacks.
|
||||
*/
|
||||
|
||||
static guint toplevel_callback_idle_id;
|
||||
static int idle_fn_scheduled;
|
||||
|
||||
static void notify_toplevel_callback(void *);
|
||||
|
||||
/*
|
||||
* Replacement code for the gtk_quit_add() function, which GTK2 - in
|
||||
* their unbounded wisdom - deprecated without providing any usable
|
||||
* replacement, and which we were using to ensure that our idle
|
||||
* function for toplevel callbacks was only run from the outermost
|
||||
* gtk_main().
|
||||
*
|
||||
* We must make sure that all our subsidiary calls to gtk_main() are
|
||||
* followed by a call to post_main(), so that the idle function can be
|
||||
* re-established when we end up back at the top level.
|
||||
*/
|
||||
void post_main(void)
|
||||
{
|
||||
if (gtk_main_level() == 1)
|
||||
notify_toplevel_callback(NULL);
|
||||
}
|
||||
|
||||
static gint idle_toplevel_callback_func(gpointer data)
|
||||
{
|
||||
if (gtk_main_level() > 1) {
|
||||
/*
|
||||
* We don't run callbacks if we're in the middle of a
|
||||
* subsidiary gtk_main. So unschedule this idle function; it
|
||||
* will be rescheduled by post_main() when we come back up a
|
||||
* level, which is the earliest we might actually do
|
||||
* something.
|
||||
*/
|
||||
if (idle_fn_scheduled) { /* double-check, just in case */
|
||||
g_source_remove(toplevel_callback_idle_id);
|
||||
idle_fn_scheduled = FALSE;
|
||||
}
|
||||
} else {
|
||||
run_toplevel_callbacks();
|
||||
}
|
||||
|
||||
/*
|
||||
* If we've emptied our toplevel callback queue, unschedule
|
||||
* ourself. Otherwise, leave ourselves pending so we'll be called
|
||||
* again to deal with more callbacks after another round of the
|
||||
* event loop.
|
||||
*/
|
||||
if (!toplevel_callback_pending() && idle_fn_scheduled) {
|
||||
g_source_remove(toplevel_callback_idle_id);
|
||||
idle_fn_scheduled = FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void notify_toplevel_callback(void *frontend)
|
||||
{
|
||||
if (!idle_fn_scheduled) {
|
||||
toplevel_callback_idle_id =
|
||||
g_idle_add(idle_toplevel_callback_func, NULL);
|
||||
idle_fn_scheduled = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Setup function. The real main program must call this.
|
||||
*/
|
||||
|
||||
void gtkcomm_setup(void)
|
||||
{
|
||||
uxsel_init();
|
||||
request_callback_notifications(notify_toplevel_callback, NULL);
|
||||
}
|
|
@ -0,0 +1,617 @@
|
|||
/*
|
||||
* gtkmain.c: the common main-program code between the straight-up
|
||||
* Unix PuTTY and pterm, which they do not share with the
|
||||
* multi-session gtkapp.c.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <gtk/gtk.h>
|
||||
#if !GTK_CHECK_VERSION(3,0,0)
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#endif
|
||||
|
||||
#if GTK_CHECK_VERSION(2,0,0)
|
||||
#include <gtk/gtkimmodule.h>
|
||||
#endif
|
||||
|
||||
#define MAY_REFER_TO_GTK_IN_HEADERS
|
||||
|
||||
#include "putty.h"
|
||||
#include "terminal.h"
|
||||
#include "gtkcompat.h"
|
||||
#include "gtkfont.h"
|
||||
#include "gtkmisc.h"
|
||||
|
||||
#ifndef NOT_X_WINDOWS
|
||||
#include <gdk/gdkx.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#endif
|
||||
|
||||
static char *progname, **gtkargvstart;
|
||||
static int ngtkargs;
|
||||
|
||||
extern char **pty_argv; /* declared in pty.c */
|
||||
extern int use_pty_argv;
|
||||
|
||||
static const char *app_name = "pterm";
|
||||
|
||||
char *x_get_default(const char *key)
|
||||
{
|
||||
#ifndef NOT_X_WINDOWS
|
||||
return XGetDefault(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
|
||||
app_name, key);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void fork_and_exec_self(int fd_to_close, ...)
|
||||
{
|
||||
/*
|
||||
* Re-execing ourself is not an exact science under Unix. I do
|
||||
* the best I can by using /proc/self/exe if available and by
|
||||
* assuming argv[0] can be found on $PATH if not.
|
||||
*
|
||||
* Note that we also have to reconstruct the elements of the
|
||||
* original argv which gtk swallowed, since the user wants the
|
||||
* new session to appear on the same X display as the old one.
|
||||
*/
|
||||
char **args;
|
||||
va_list ap;
|
||||
int i, n;
|
||||
int pid;
|
||||
|
||||
/*
|
||||
* Collect the arguments with which to re-exec ourself.
|
||||
*/
|
||||
va_start(ap, fd_to_close);
|
||||
n = 2; /* progname and terminating NULL */
|
||||
n += ngtkargs;
|
||||
while (va_arg(ap, char *) != NULL)
|
||||
n++;
|
||||
va_end(ap);
|
||||
|
||||
args = snewn(n, char *);
|
||||
args[0] = progname;
|
||||
args[n-1] = NULL;
|
||||
for (i = 0; i < ngtkargs; i++)
|
||||
args[i+1] = gtkargvstart[i];
|
||||
|
||||
i++;
|
||||
va_start(ap, fd_to_close);
|
||||
while ((args[i++] = va_arg(ap, char *)) != NULL);
|
||||
va_end(ap);
|
||||
|
||||
assert(i == n);
|
||||
|
||||
/*
|
||||
* Do the double fork.
|
||||
*/
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
perror("fork");
|
||||
sfree(args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
int pid2 = fork();
|
||||
if (pid2 < 0) {
|
||||
perror("fork");
|
||||
_exit(1);
|
||||
} else if (pid2 > 0) {
|
||||
/*
|
||||
* First child has successfully forked second child. My
|
||||
* Work Here Is Done. Note the use of _exit rather than
|
||||
* exit: the latter appears to cause destroy messages
|
||||
* to be sent to the X server. I suspect gtk uses
|
||||
* atexit.
|
||||
*/
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we reach here, we are the second child, so we now
|
||||
* actually perform the exec.
|
||||
*/
|
||||
if (fd_to_close >= 0)
|
||||
close(fd_to_close);
|
||||
|
||||
execv("/proc/self/exe", args);
|
||||
execvp(progname, args);
|
||||
perror("exec");
|
||||
_exit(127);
|
||||
|
||||
} else {
|
||||
int status;
|
||||
sfree(args);
|
||||
waitpid(pid, &status, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void launch_duplicate_session(Conf *conf)
|
||||
{
|
||||
/*
|
||||
* For this feature we must marshal conf and (possibly) pty_argv
|
||||
* into a byte stream, create a pipe, and send this byte stream
|
||||
* to the child through the pipe.
|
||||
*/
|
||||
int i, ret, sersize, size;
|
||||
char *data;
|
||||
char option[80];
|
||||
int pipefd[2];
|
||||
|
||||
if (pipe(pipefd) < 0) {
|
||||
perror("pipe");
|
||||
return;
|
||||
}
|
||||
|
||||
size = sersize = conf_serialised_size(conf);
|
||||
if (use_pty_argv && pty_argv) {
|
||||
for (i = 0; pty_argv[i]; i++)
|
||||
size += strlen(pty_argv[i]) + 1;
|
||||
}
|
||||
|
||||
data = snewn(size, char);
|
||||
conf_serialise(conf, data);
|
||||
if (use_pty_argv && pty_argv) {
|
||||
int p = sersize;
|
||||
for (i = 0; pty_argv[i]; i++) {
|
||||
strcpy(data + p, pty_argv[i]);
|
||||
p += strlen(pty_argv[i]) + 1;
|
||||
}
|
||||
assert(p == size);
|
||||
}
|
||||
|
||||
sprintf(option, "---[%d,%d]", pipefd[0], size);
|
||||
noncloexec(pipefd[0]);
|
||||
fork_and_exec_self(pipefd[1], option, NULL);
|
||||
close(pipefd[0]);
|
||||
|
||||
i = ret = 0;
|
||||
while (i < size && (ret = write(pipefd[1], data + i, size - i)) > 0)
|
||||
i += ret;
|
||||
if (ret < 0)
|
||||
perror("write to pipe");
|
||||
close(pipefd[1]);
|
||||
sfree(data);
|
||||
}
|
||||
|
||||
void launch_new_session(void)
|
||||
{
|
||||
fork_and_exec_self(-1, NULL);
|
||||
}
|
||||
|
||||
void launch_saved_session(const char *str)
|
||||
{
|
||||
fork_and_exec_self(-1, "-load", str, NULL);
|
||||
}
|
||||
|
||||
int read_dupsession_data(Conf *conf, char *arg)
|
||||
{
|
||||
int fd, i, ret, size, size_used;
|
||||
char *data;
|
||||
|
||||
if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) {
|
||||
fprintf(stderr, "%s: malformed magic argument `%s'\n", appname, arg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
data = snewn(size, char);
|
||||
i = ret = 0;
|
||||
while (i < size && (ret = read(fd, data + i, size - i)) > 0)
|
||||
i += ret;
|
||||
if (ret < 0) {
|
||||
perror("read from pipe");
|
||||
exit(1);
|
||||
} else if (i < size) {
|
||||
fprintf(stderr, "%s: unexpected EOF in Duplicate Session data\n",
|
||||
appname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
size_used = conf_deserialise(conf, data, size);
|
||||
if (use_pty_argv && size > size_used) {
|
||||
int n = 0;
|
||||
i = size_used;
|
||||
while (i < size) {
|
||||
while (i < size && data[i]) i++;
|
||||
if (i >= size) {
|
||||
fprintf(stderr, "%s: malformed Duplicate Session data\n",
|
||||
appname);
|
||||
exit(1);
|
||||
}
|
||||
i++;
|
||||
n++;
|
||||
}
|
||||
pty_argv = snewn(n+1, char *);
|
||||
pty_argv[n] = NULL;
|
||||
n = 0;
|
||||
i = size_used;
|
||||
while (i < size) {
|
||||
char *p = data + i;
|
||||
while (i < size && data[i]) i++;
|
||||
assert(i < size);
|
||||
i++;
|
||||
pty_argv[n++] = dupstr(p);
|
||||
}
|
||||
}
|
||||
|
||||
sfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void help(FILE *fp) {
|
||||
if(fprintf(fp,
|
||||
"pterm option summary:\n"
|
||||
"\n"
|
||||
" --display DISPLAY Specify X display to use (note '--')\n"
|
||||
" -name PREFIX Prefix when looking up resources (default: pterm)\n"
|
||||
" -fn FONT Normal text font\n"
|
||||
" -fb FONT Bold text font\n"
|
||||
" -geometry GEOMETRY Position and size of window (size in characters)\n"
|
||||
" -sl LINES Number of lines of scrollback\n"
|
||||
" -fg COLOUR, -bg COLOUR Foreground/background colour\n"
|
||||
" -bfg COLOUR, -bbg COLOUR Foreground/background bold colour\n"
|
||||
" -cfg COLOUR, -bfg COLOUR Foreground/background cursor colour\n"
|
||||
" -T TITLE Window title\n"
|
||||
" -ut, +ut Do(default) or do not update utmp\n"
|
||||
" -ls, +ls Do(default) or do not make shell a login shell\n"
|
||||
" -sb, +sb Do(default) or do not display a scrollbar\n"
|
||||
" -log PATH, -sessionlog PATH Log all output to a file\n"
|
||||
" -nethack Map numeric keypad to hjklyubn direction keys\n"
|
||||
" -xrm RESOURCE-STRING Set an X resource\n"
|
||||
" -e COMMAND [ARGS...] Execute command (consumes all remaining args)\n"
|
||||
) < 0 || fflush(fp) < 0) {
|
||||
perror("output error");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void version(FILE *fp) {
|
||||
if(fprintf(fp, "%s: %s\n", appname, ver) < 0 || fflush(fp) < 0) {
|
||||
perror("output error");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static struct gui_data *the_inst;
|
||||
|
||||
static const char *geometry_string;
|
||||
|
||||
int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
|
||||
Conf *conf)
|
||||
{
|
||||
int err = 0;
|
||||
char *val;
|
||||
|
||||
/*
|
||||
* Macros to make argument handling easier. Note that because
|
||||
* they need to call `continue', they cannot be contained in
|
||||
* the usual do {...} while (0) wrapper to make them
|
||||
* syntactically single statements; hence it is not legal to
|
||||
* use one of these macros as an unbraced statement between
|
||||
* `if' and `else'.
|
||||
*/
|
||||
#define EXPECTS_ARG { \
|
||||
if (--argc <= 0) { \
|
||||
err = 1; \
|
||||
fprintf(stderr, "%s: %s expects an argument\n", appname, p); \
|
||||
continue; \
|
||||
} else \
|
||||
val = *++argv; \
|
||||
}
|
||||
#define SECOND_PASS_ONLY { if (!do_everything) continue; }
|
||||
|
||||
while (--argc > 0) {
|
||||
const char *p = *++argv;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Shameless cheating. Debian requires all X terminal
|
||||
* emulators to support `-T title'; but
|
||||
* cmdline_process_param will eat -T (it means no-pty) and
|
||||
* complain that pterm doesn't support it. So, in pterm
|
||||
* only, we convert -T into -title.
|
||||
*/
|
||||
if ((cmdline_tooltype & TOOLTYPE_NONNETWORK) &&
|
||||
!strcmp(p, "-T"))
|
||||
p = "-title";
|
||||
|
||||
ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
|
||||
do_everything ? 1 : -1, conf);
|
||||
|
||||
if (ret == -2) {
|
||||
cmdline_error("option \"%s\" requires an argument", p);
|
||||
} else if (ret == 2) {
|
||||
--argc, ++argv; /* skip next argument */
|
||||
continue;
|
||||
} else if (ret == 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(p, "-fn") || !strcmp(p, "-font")) {
|
||||
FontSpec *fs;
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
fs = fontspec_new(val);
|
||||
conf_set_fontspec(conf, CONF_font, fs);
|
||||
fontspec_free(fs);
|
||||
|
||||
} else if (!strcmp(p, "-fb")) {
|
||||
FontSpec *fs;
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
fs = fontspec_new(val);
|
||||
conf_set_fontspec(conf, CONF_boldfont, fs);
|
||||
fontspec_free(fs);
|
||||
|
||||
} else if (!strcmp(p, "-fw")) {
|
||||
FontSpec *fs;
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
fs = fontspec_new(val);
|
||||
conf_set_fontspec(conf, CONF_widefont, fs);
|
||||
fontspec_free(fs);
|
||||
|
||||
} else if (!strcmp(p, "-fwb")) {
|
||||
FontSpec *fs;
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
fs = fontspec_new(val);
|
||||
conf_set_fontspec(conf, CONF_wideboldfont, fs);
|
||||
fontspec_free(fs);
|
||||
|
||||
} else if (!strcmp(p, "-cs")) {
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_str(conf, CONF_line_codepage, val);
|
||||
|
||||
} else if (!strcmp(p, "-geometry")) {
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
geometry_string = val;
|
||||
} else if (!strcmp(p, "-sl")) {
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_savelines, atoi(val));
|
||||
|
||||
} else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") ||
|
||||
!strcmp(p, "-bfg") || !strcmp(p, "-bbg") ||
|
||||
!strcmp(p, "-cfg") || !strcmp(p, "-cbg")) {
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
|
||||
{
|
||||
#if GTK_CHECK_VERSION(3,0,0)
|
||||
GdkRGBA rgba;
|
||||
int success = gdk_rgba_parse(&rgba, val);
|
||||
#else
|
||||
GdkColor col;
|
||||
int success = gdk_color_parse(val, &col);
|
||||
#endif
|
||||
|
||||
if (!success) {
|
||||
err = 1;
|
||||
fprintf(stderr, "%s: unable to parse colour \"%s\"\n",
|
||||
appname, val);
|
||||
} else {
|
||||
#if GTK_CHECK_VERSION(3,0,0)
|
||||
int r = rgba.red * 255;
|
||||
int g = rgba.green * 255;
|
||||
int b = rgba.blue * 255;
|
||||
#else
|
||||
int r = col.red / 256;
|
||||
int g = col.green / 256;
|
||||
int b = col.blue / 256;
|
||||
#endif
|
||||
|
||||
int index;
|
||||
index = (!strcmp(p, "-fg") ? 0 :
|
||||
!strcmp(p, "-bg") ? 2 :
|
||||
!strcmp(p, "-bfg") ? 1 :
|
||||
!strcmp(p, "-bbg") ? 3 :
|
||||
!strcmp(p, "-cfg") ? 4 :
|
||||
!strcmp(p, "-cbg") ? 5 : -1);
|
||||
assert(index != -1);
|
||||
|
||||
conf_set_int_int(conf, CONF_colours, index*3+0, r);
|
||||
conf_set_int_int(conf, CONF_colours, index*3+1, g);
|
||||
conf_set_int_int(conf, CONF_colours, index*3+2, b);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (use_pty_argv && !strcmp(p, "-e")) {
|
||||
/* This option swallows all further arguments. */
|
||||
if (!do_everything)
|
||||
break;
|
||||
|
||||
if (--argc > 0) {
|
||||
int i;
|
||||
pty_argv = snewn(argc+1, char *);
|
||||
++argv;
|
||||
for (i = 0; i < argc; i++)
|
||||
pty_argv[i] = argv[i];
|
||||
pty_argv[argc] = NULL;
|
||||
break; /* finished command-line processing */
|
||||
} else
|
||||
err = 1, fprintf(stderr, "%s: -e expects an argument\n",
|
||||
appname);
|
||||
|
||||
} else if (!strcmp(p, "-title")) {
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_str(conf, CONF_wintitle, val);
|
||||
|
||||
} else if (!strcmp(p, "-log")) {
|
||||
Filename *fn;
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
fn = filename_from_str(val);
|
||||
conf_set_filename(conf, CONF_logfilename, fn);
|
||||
conf_set_int(conf, CONF_logtype, LGTYP_DEBUG);
|
||||
filename_free(fn);
|
||||
|
||||
} else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) {
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_stamp_utmp, 0);
|
||||
|
||||
} else if (!strcmp(p, "-ut")) {
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_stamp_utmp, 1);
|
||||
|
||||
} else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) {
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_login_shell, 0);
|
||||
|
||||
} else if (!strcmp(p, "-ls")) {
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_login_shell, 1);
|
||||
|
||||
} else if (!strcmp(p, "-nethack")) {
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_nethack_keypad, 1);
|
||||
|
||||
} else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) {
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_scrollbar, 0);
|
||||
|
||||
} else if (!strcmp(p, "-sb")) {
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_scrollbar, 1);
|
||||
|
||||
} else if (!strcmp(p, "-name")) {
|
||||
EXPECTS_ARG;
|
||||
app_name = val;
|
||||
|
||||
} else if (!strcmp(p, "-xrm")) {
|
||||
EXPECTS_ARG;
|
||||
provide_xrm_string(val);
|
||||
|
||||
} else if(!strcmp(p, "-help") || !strcmp(p, "--help")) {
|
||||
help(stdout);
|
||||
exit(0);
|
||||
|
||||
} else if(!strcmp(p, "-version") || !strcmp(p, "--version")) {
|
||||
version(stdout);
|
||||
exit(0);
|
||||
|
||||
} else if (!strcmp(p, "-pgpfp")) {
|
||||
pgp_fingerprints();
|
||||
exit(1);
|
||||
|
||||
} else if(p[0] != '-' && (!do_everything ||
|
||||
process_nonoption_arg(p, conf,
|
||||
allow_launch))) {
|
||||
/* do nothing */
|
||||
|
||||
} else {
|
||||
err = 1;
|
||||
fprintf(stderr, "%s: unrecognized option '%s'\n", appname, p);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
extern int cfgbox(Conf *conf);
|
||||
|
||||
int pt_main(int argc, char **argv)
|
||||
{
|
||||
Conf *conf;
|
||||
int need_config_box;
|
||||
|
||||
setlocale(LC_CTYPE, "");
|
||||
|
||||
progname = argv[0];
|
||||
|
||||
/*
|
||||
* Copy the original argv before letting gtk_init fiddle with
|
||||
* it. It will be required later.
|
||||
*/
|
||||
{
|
||||
int i, oldargc;
|
||||
gtkargvstart = snewn(argc-1, char *);
|
||||
for (i = 1; i < argc; i++)
|
||||
gtkargvstart[i-1] = dupstr(argv[i]);
|
||||
oldargc = argc;
|
||||
gtk_init(&argc, &argv);
|
||||
ngtkargs = oldargc - argc;
|
||||
}
|
||||
|
||||
conf = conf_new();
|
||||
|
||||
gtkcomm_setup();
|
||||
|
||||
/*
|
||||
* Block SIGPIPE: if we attempt Duplicate Session or similar and
|
||||
* it falls over in some way, we certainly don't want SIGPIPE
|
||||
* terminating the main pterm/PuTTY. However, we'll have to
|
||||
* unblock it again when pterm forks.
|
||||
*/
|
||||
block_signal(SIGPIPE, 1);
|
||||
|
||||
if (argc > 1 && !strncmp(argv[1], "---", 3)) {
|
||||
read_dupsession_data(conf, argv[1]);
|
||||
/* Splatter this argument so it doesn't clutter a ps listing */
|
||||
smemclr(argv[1], strlen(argv[1]));
|
||||
|
||||
assert(conf_launchable(conf));
|
||||
need_config_box = FALSE;
|
||||
} else {
|
||||
/* By default, we bring up the config dialog, rather than launching
|
||||
* a session. This gets set to TRUE if something happens to change
|
||||
* that (e.g., a hostname is specified on the command-line). */
|
||||
int allow_launch = FALSE;
|
||||
if (do_cmdline(argc, argv, 0, &allow_launch, conf))
|
||||
exit(1); /* pre-defaults pass to get -class */
|
||||
do_defaults(NULL, conf);
|
||||
if (do_cmdline(argc, argv, 1, &allow_launch, conf))
|
||||
exit(1); /* post-defaults, do everything */
|
||||
|
||||
cmdline_run_saved(conf);
|
||||
|
||||
if (loaded_session)
|
||||
allow_launch = TRUE;
|
||||
|
||||
need_config_box = (!allow_launch || !conf_launchable(conf));
|
||||
}
|
||||
|
||||
/*
|
||||
* Put up the config box.
|
||||
*/
|
||||
if (need_config_box && !cfgbox(conf))
|
||||
exit(0); /* config box hit Cancel */
|
||||
|
||||
/*
|
||||
* Create the main session window. We don't really need to keep
|
||||
* the return value - the fact that it'll be linked from a zillion
|
||||
* GTK and glib bits and bobs known to the main loop will be
|
||||
* sufficient to make everything actually happen - but we stash it
|
||||
* in a global variable anyway, so that it'll be easy to find in a
|
||||
* debugger.
|
||||
*/
|
||||
the_inst = new_session_window(conf, geometry_string);
|
||||
|
||||
gtk_main();
|
||||
|
||||
return 0;
|
||||
}
|
850
unix/gtkwin.c
850
unix/gtkwin.c
|
@ -46,36 +46,12 @@
|
|||
|
||||
#include "x11misc.h"
|
||||
|
||||
#define CAT2(x,y) x ## y
|
||||
#define CAT(x,y) CAT2(x,y)
|
||||
#define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)}
|
||||
|
||||
#if GTK_CHECK_VERSION(2,0,0)
|
||||
ASSERT(sizeof(long) <= sizeof(gsize));
|
||||
#define LONG_TO_GPOINTER(l) GSIZE_TO_POINTER(l)
|
||||
#define GPOINTER_TO_LONG(p) GPOINTER_TO_SIZE(p)
|
||||
#else /* Gtk 1.2 */
|
||||
ASSERT(sizeof(long) <= sizeof(gpointer));
|
||||
#define LONG_TO_GPOINTER(l) ((gpointer)(long)(l))
|
||||
#define GPOINTER_TO_LONG(p) ((long)(p))
|
||||
#endif
|
||||
|
||||
/* Colours come in two flavours: configurable, and xterm-extended. */
|
||||
#define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */
|
||||
#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)
|
||||
|
||||
GdkAtom compound_text_atom, utf8_string_atom;
|
||||
|
||||
extern char **pty_argv; /* declared in pty.c */
|
||||
extern int use_pty_argv;
|
||||
|
||||
/*
|
||||
* Timers are global across all sessions (even if we were handling
|
||||
* multiple sessions, which we aren't), so the current timer ID is
|
||||
* a global variable.
|
||||
*/
|
||||
static guint timer_id = 0;
|
||||
|
||||
struct clipboard_data_instance;
|
||||
|
||||
struct gui_data {
|
||||
|
@ -142,8 +118,6 @@ struct gui_data {
|
|||
int ignore_sbar;
|
||||
int mouseptr_visible;
|
||||
int busy_status;
|
||||
guint toplevel_callback_idle_id;
|
||||
int idle_fn_scheduled, quit_fn_scheduled;
|
||||
int alt_keycode;
|
||||
int alt_digits;
|
||||
char *wintitle;
|
||||
|
@ -158,8 +132,6 @@ struct gui_data {
|
|||
struct unicode_data ucsdata;
|
||||
Conf *conf;
|
||||
void *eventlogstuff;
|
||||
char *progname, **gtkargvstart;
|
||||
int ngtkargs;
|
||||
guint32 input_event_time; /* Timestamp of the most recent input event. */
|
||||
int reconfiguring;
|
||||
#if GTK_CHECK_VERSION(3,4,0)
|
||||
|
@ -196,21 +168,9 @@ struct draw_ctx {
|
|||
|
||||
static int send_raw_mouse;
|
||||
|
||||
static const char *app_name = "pterm";
|
||||
|
||||
static void start_backend(struct gui_data *inst);
|
||||
static void exit_callback(void *vinst);
|
||||
|
||||
char *x_get_default(const char *key)
|
||||
{
|
||||
#ifndef NOT_X_WINDOWS
|
||||
return XGetDefault(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
|
||||
app_name, key);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void connection_fatal(void *frontend, const char *p, ...)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)frontend;
|
||||
|
@ -2014,48 +1974,6 @@ static void exit_callback(void *vinst)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Replacement code for the gtk_quit_add() function, which GTK2 - in
|
||||
* their unbounded wisdom - deprecated without providing any usable
|
||||
* replacement, and which we were using to ensure that our idle
|
||||
* function for toplevel callbacks was only run from the outermost
|
||||
* gtk_main().
|
||||
*
|
||||
* We maintain a global variable with a list of 'struct gui_data'
|
||||
* instances on which we should call inst_post_main() when a
|
||||
* subsidiary gtk_main() terminates; then we must make sure that all
|
||||
* our subsidiary calls to gtk_main() are followed by a call to
|
||||
* post_main().
|
||||
*
|
||||
* This is kind of overkill in the sense that at the time of writing
|
||||
* we don't actually ever run more than one 'struct gui_data' instance
|
||||
* in the same process, but we're _so nearly_ prepared to do that that
|
||||
* I want to remain futureproof against the possibility of doing so in
|
||||
* future.
|
||||
*/
|
||||
struct post_main_context {
|
||||
struct post_main_context *next;
|
||||
struct gui_data *inst;
|
||||
};
|
||||
struct post_main_context *post_main_list_head = NULL;
|
||||
static void request_post_main(struct gui_data *inst)
|
||||
{
|
||||
struct post_main_context *node = snew(struct post_main_context);
|
||||
node->next = post_main_list_head;
|
||||
node->inst = inst;
|
||||
post_main_list_head = node;
|
||||
}
|
||||
static void inst_post_main(struct gui_data *inst);
|
||||
void post_main(void)
|
||||
{
|
||||
while (post_main_list_head) {
|
||||
struct post_main_context *node = post_main_list_head;
|
||||
post_main_list_head = node->next;
|
||||
inst_post_main(node->inst);
|
||||
sfree(node);
|
||||
}
|
||||
}
|
||||
|
||||
void notify_remote_exit(void *frontend)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)frontend;
|
||||
|
@ -2063,157 +1981,6 @@ void notify_remote_exit(void *frontend)
|
|||
queue_toplevel_callback(exit_callback, inst);
|
||||
}
|
||||
|
||||
static void notify_toplevel_callback(void *frontend);
|
||||
|
||||
static void inst_post_main(struct gui_data *inst)
|
||||
{
|
||||
if (gtk_main_level() == 1) {
|
||||
notify_toplevel_callback(inst);
|
||||
inst->quit_fn_scheduled = FALSE;
|
||||
} else {
|
||||
/* Apparently we're _still_ more than one level deep in
|
||||
* gtk_main() instances, so we'll need another callback for
|
||||
* when we get out of the next one. */
|
||||
request_post_main(inst);
|
||||
}
|
||||
}
|
||||
|
||||
static gint idle_toplevel_callback_func(gpointer data)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)data;
|
||||
|
||||
if (gtk_main_level() > 1) {
|
||||
/*
|
||||
* We don't run the callbacks if we're in the middle of a
|
||||
* subsidiary gtk_main. Instead, ask for a callback when we
|
||||
* get back out of the subsidiary main loop (if we haven't
|
||||
* already arranged one), so we can reschedule ourself then.
|
||||
*/
|
||||
if (!inst->quit_fn_scheduled) {
|
||||
request_post_main(inst);
|
||||
inst->quit_fn_scheduled = TRUE;
|
||||
}
|
||||
/*
|
||||
* And unschedule this idle function, since we've now done
|
||||
* everything we can until the innermost gtk_main has quit and
|
||||
* can reschedule us with a chance of actually taking action.
|
||||
*/
|
||||
if (inst->idle_fn_scheduled) { /* double-check, just in case */
|
||||
g_source_remove(inst->toplevel_callback_idle_id);
|
||||
inst->idle_fn_scheduled = FALSE;
|
||||
}
|
||||
} else {
|
||||
run_toplevel_callbacks();
|
||||
}
|
||||
|
||||
/*
|
||||
* If we've emptied our toplevel callback queue, unschedule
|
||||
* ourself. Otherwise, leave ourselves pending so we'll be called
|
||||
* again to deal with more callbacks after another round of the
|
||||
* event loop.
|
||||
*/
|
||||
if (!toplevel_callback_pending() && inst->idle_fn_scheduled) {
|
||||
g_source_remove(inst->toplevel_callback_idle_id);
|
||||
inst->idle_fn_scheduled = FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void notify_toplevel_callback(void *frontend)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)frontend;
|
||||
|
||||
if (!inst->idle_fn_scheduled) {
|
||||
inst->toplevel_callback_idle_id =
|
||||
g_idle_add(idle_toplevel_callback_func, inst);
|
||||
inst->idle_fn_scheduled = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static gint timer_trigger(gpointer data)
|
||||
{
|
||||
unsigned long now = GPOINTER_TO_LONG(data);
|
||||
unsigned long next, then;
|
||||
long ticks;
|
||||
|
||||
/*
|
||||
* Destroy the timer we got here on.
|
||||
*/
|
||||
if (timer_id) {
|
||||
g_source_remove(timer_id);
|
||||
timer_id = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* run_timers() may cause a call to timer_change_notify, in which
|
||||
* case a new timer will already have been set up and left in
|
||||
* timer_id. If it hasn't, and run_timers reports that some timing
|
||||
* still needs to be done, we do it ourselves.
|
||||
*/
|
||||
if (run_timers(now, &next) && !timer_id) {
|
||||
then = now;
|
||||
now = GETTICKCOUNT();
|
||||
if (now - then > next - then)
|
||||
ticks = 0;
|
||||
else
|
||||
ticks = next - now;
|
||||
timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returning FALSE means 'don't call this timer again', which
|
||||
* _should_ be redundant given that we removed it above, but just
|
||||
* in case, return FALSE anyway.
|
||||
*/
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void timer_change_notify(unsigned long next)
|
||||
{
|
||||
long ticks;
|
||||
|
||||
if (timer_id)
|
||||
g_source_remove(timer_id);
|
||||
|
||||
ticks = next - GETTICKCOUNT();
|
||||
if (ticks <= 0)
|
||||
ticks = 1; /* just in case */
|
||||
|
||||
timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next));
|
||||
}
|
||||
|
||||
#if GTK_CHECK_VERSION(2,0,0)
|
||||
gboolean fd_input_func(GIOChannel *source, GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
int sourcefd = g_io_channel_unix_get_fd(source);
|
||||
/*
|
||||
* We must process exceptional notifications before ordinary
|
||||
* readability ones, or we may go straight past the urgent
|
||||
* marker.
|
||||
*/
|
||||
if (condition & G_IO_PRI)
|
||||
select_result(sourcefd, 4);
|
||||
if (condition & G_IO_IN)
|
||||
select_result(sourcefd, 1);
|
||||
if (condition & G_IO_OUT)
|
||||
select_result(sourcefd, 2);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#else
|
||||
void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
|
||||
{
|
||||
if (condition & GDK_INPUT_EXCEPTION)
|
||||
select_result(sourcefd, 4);
|
||||
if (condition & GDK_INPUT_READ)
|
||||
select_result(sourcefd, 1);
|
||||
if (condition & GDK_INPUT_WRITE)
|
||||
select_result(sourcefd, 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
void destroy(GtkWidget *widget, gpointer data)
|
||||
{
|
||||
gtk_main_quit();
|
||||
|
@ -3742,341 +3509,6 @@ long get_windowid(void *frontend)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void help(FILE *fp) {
|
||||
if(fprintf(fp,
|
||||
"pterm option summary:\n"
|
||||
"\n"
|
||||
" --display DISPLAY Specify X display to use (note '--')\n"
|
||||
" -name PREFIX Prefix when looking up resources (default: pterm)\n"
|
||||
" -fn FONT Normal text font\n"
|
||||
" -fb FONT Bold text font\n"
|
||||
" -geometry GEOMETRY Position and size of window (size in characters)\n"
|
||||
" -sl LINES Number of lines of scrollback\n"
|
||||
" -fg COLOUR, -bg COLOUR Foreground/background colour\n"
|
||||
" -bfg COLOUR, -bbg COLOUR Foreground/background bold colour\n"
|
||||
" -cfg COLOUR, -bfg COLOUR Foreground/background cursor colour\n"
|
||||
" -T TITLE Window title\n"
|
||||
" -ut, +ut Do(default) or do not update utmp\n"
|
||||
" -ls, +ls Do(default) or do not make shell a login shell\n"
|
||||
" -sb, +sb Do(default) or do not display a scrollbar\n"
|
||||
" -log PATH, -sessionlog PATH Log all output to a file\n"
|
||||
" -nethack Map numeric keypad to hjklyubn direction keys\n"
|
||||
" -xrm RESOURCE-STRING Set an X resource\n"
|
||||
" -e COMMAND [ARGS...] Execute command (consumes all remaining args)\n"
|
||||
) < 0 || fflush(fp) < 0) {
|
||||
perror("output error");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void version(FILE *fp) {
|
||||
if(fprintf(fp, "%s: %s\n", appname, ver) < 0 || fflush(fp) < 0) {
|
||||
perror("output error");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
|
||||
struct gui_data *inst, Conf *conf)
|
||||
{
|
||||
int err = 0;
|
||||
char *val;
|
||||
|
||||
/*
|
||||
* Macros to make argument handling easier. Note that because
|
||||
* they need to call `continue', they cannot be contained in
|
||||
* the usual do {...} while (0) wrapper to make them
|
||||
* syntactically single statements; hence it is not legal to
|
||||
* use one of these macros as an unbraced statement between
|
||||
* `if' and `else'.
|
||||
*/
|
||||
#define EXPECTS_ARG { \
|
||||
if (--argc <= 0) { \
|
||||
err = 1; \
|
||||
fprintf(stderr, "%s: %s expects an argument\n", appname, p); \
|
||||
continue; \
|
||||
} else \
|
||||
val = *++argv; \
|
||||
}
|
||||
#define SECOND_PASS_ONLY { if (!do_everything) continue; }
|
||||
|
||||
while (--argc > 0) {
|
||||
const char *p = *++argv;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Shameless cheating. Debian requires all X terminal
|
||||
* emulators to support `-T title'; but
|
||||
* cmdline_process_param will eat -T (it means no-pty) and
|
||||
* complain that pterm doesn't support it. So, in pterm
|
||||
* only, we convert -T into -title.
|
||||
*/
|
||||
if ((cmdline_tooltype & TOOLTYPE_NONNETWORK) &&
|
||||
!strcmp(p, "-T"))
|
||||
p = "-title";
|
||||
|
||||
ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
|
||||
do_everything ? 1 : -1, conf);
|
||||
|
||||
if (ret == -2) {
|
||||
cmdline_error("option \"%s\" requires an argument", p);
|
||||
} else if (ret == 2) {
|
||||
--argc, ++argv; /* skip next argument */
|
||||
continue;
|
||||
} else if (ret == 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(p, "-fn") || !strcmp(p, "-font")) {
|
||||
FontSpec *fs;
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
fs = fontspec_new(val);
|
||||
conf_set_fontspec(conf, CONF_font, fs);
|
||||
fontspec_free(fs);
|
||||
|
||||
} else if (!strcmp(p, "-fb")) {
|
||||
FontSpec *fs;
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
fs = fontspec_new(val);
|
||||
conf_set_fontspec(conf, CONF_boldfont, fs);
|
||||
fontspec_free(fs);
|
||||
|
||||
} else if (!strcmp(p, "-fw")) {
|
||||
FontSpec *fs;
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
fs = fontspec_new(val);
|
||||
conf_set_fontspec(conf, CONF_widefont, fs);
|
||||
fontspec_free(fs);
|
||||
|
||||
} else if (!strcmp(p, "-fwb")) {
|
||||
FontSpec *fs;
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
fs = fontspec_new(val);
|
||||
conf_set_fontspec(conf, CONF_wideboldfont, fs);
|
||||
fontspec_free(fs);
|
||||
|
||||
} else if (!strcmp(p, "-cs")) {
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_str(conf, CONF_line_codepage, val);
|
||||
|
||||
} else if (!strcmp(p, "-geometry")) {
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
|
||||
#if GTK_CHECK_VERSION(2,0,0)
|
||||
inst->geometry = val;
|
||||
#else
|
||||
/* On GTK 1, we have to do this using raw Xlib */
|
||||
{
|
||||
int flags, x, y;
|
||||
unsigned int w, h;
|
||||
flags = XParseGeometry(val, &x, &y, &w, &h);
|
||||
if (flags & WidthValue)
|
||||
conf_set_int(conf, CONF_width, w);
|
||||
if (flags & HeightValue)
|
||||
conf_set_int(conf, CONF_height, h);
|
||||
|
||||
if (flags & (XValue | YValue)) {
|
||||
inst->xpos = x;
|
||||
inst->ypos = y;
|
||||
inst->gotpos = TRUE;
|
||||
inst->gravity = ((flags & XNegative ? 1 : 0) |
|
||||
(flags & YNegative ? 2 : 0));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} else if (!strcmp(p, "-sl")) {
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_savelines, atoi(val));
|
||||
|
||||
} else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") ||
|
||||
!strcmp(p, "-bfg") || !strcmp(p, "-bbg") ||
|
||||
!strcmp(p, "-cfg") || !strcmp(p, "-cbg")) {
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
|
||||
{
|
||||
#if GTK_CHECK_VERSION(3,0,0)
|
||||
GdkRGBA rgba;
|
||||
int success = gdk_rgba_parse(&rgba, val);
|
||||
#else
|
||||
GdkColor col;
|
||||
int success = gdk_color_parse(val, &col);
|
||||
#endif
|
||||
|
||||
if (!success) {
|
||||
err = 1;
|
||||
fprintf(stderr, "%s: unable to parse colour \"%s\"\n",
|
||||
appname, val);
|
||||
} else {
|
||||
#if GTK_CHECK_VERSION(3,0,0)
|
||||
int r = rgba.red * 255;
|
||||
int g = rgba.green * 255;
|
||||
int b = rgba.blue * 255;
|
||||
#else
|
||||
int r = col.red / 256;
|
||||
int g = col.green / 256;
|
||||
int b = col.blue / 256;
|
||||
#endif
|
||||
|
||||
int index;
|
||||
index = (!strcmp(p, "-fg") ? 0 :
|
||||
!strcmp(p, "-bg") ? 2 :
|
||||
!strcmp(p, "-bfg") ? 1 :
|
||||
!strcmp(p, "-bbg") ? 3 :
|
||||
!strcmp(p, "-cfg") ? 4 :
|
||||
!strcmp(p, "-cbg") ? 5 : -1);
|
||||
assert(index != -1);
|
||||
|
||||
conf_set_int_int(conf, CONF_colours, index*3+0, r);
|
||||
conf_set_int_int(conf, CONF_colours, index*3+1, g);
|
||||
conf_set_int_int(conf, CONF_colours, index*3+2, b);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (use_pty_argv && !strcmp(p, "-e")) {
|
||||
/* This option swallows all further arguments. */
|
||||
if (!do_everything)
|
||||
break;
|
||||
|
||||
if (--argc > 0) {
|
||||
int i;
|
||||
pty_argv = snewn(argc+1, char *);
|
||||
++argv;
|
||||
for (i = 0; i < argc; i++)
|
||||
pty_argv[i] = argv[i];
|
||||
pty_argv[argc] = NULL;
|
||||
break; /* finished command-line processing */
|
||||
} else
|
||||
err = 1, fprintf(stderr, "%s: -e expects an argument\n",
|
||||
appname);
|
||||
|
||||
} else if (!strcmp(p, "-title")) {
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_str(conf, CONF_wintitle, val);
|
||||
|
||||
} else if (!strcmp(p, "-log")) {
|
||||
Filename *fn;
|
||||
EXPECTS_ARG;
|
||||
SECOND_PASS_ONLY;
|
||||
fn = filename_from_str(val);
|
||||
conf_set_filename(conf, CONF_logfilename, fn);
|
||||
conf_set_int(conf, CONF_logtype, LGTYP_DEBUG);
|
||||
filename_free(fn);
|
||||
|
||||
} else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) {
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_stamp_utmp, 0);
|
||||
|
||||
} else if (!strcmp(p, "-ut")) {
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_stamp_utmp, 1);
|
||||
|
||||
} else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) {
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_login_shell, 0);
|
||||
|
||||
} else if (!strcmp(p, "-ls")) {
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_login_shell, 1);
|
||||
|
||||
} else if (!strcmp(p, "-nethack")) {
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_nethack_keypad, 1);
|
||||
|
||||
} else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) {
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_scrollbar, 0);
|
||||
|
||||
} else if (!strcmp(p, "-sb")) {
|
||||
SECOND_PASS_ONLY;
|
||||
conf_set_int(conf, CONF_scrollbar, 1);
|
||||
|
||||
} else if (!strcmp(p, "-name")) {
|
||||
EXPECTS_ARG;
|
||||
app_name = val;
|
||||
|
||||
} else if (!strcmp(p, "-xrm")) {
|
||||
EXPECTS_ARG;
|
||||
provide_xrm_string(val);
|
||||
|
||||
} else if(!strcmp(p, "-help") || !strcmp(p, "--help")) {
|
||||
help(stdout);
|
||||
exit(0);
|
||||
|
||||
} else if(!strcmp(p, "-version") || !strcmp(p, "--version")) {
|
||||
version(stdout);
|
||||
exit(0);
|
||||
|
||||
} else if (!strcmp(p, "-pgpfp")) {
|
||||
pgp_fingerprints();
|
||||
exit(1);
|
||||
|
||||
} else if(p[0] != '-' && (!do_everything ||
|
||||
process_nonoption_arg(p, conf,
|
||||
allow_launch))) {
|
||||
/* do nothing */
|
||||
|
||||
} else {
|
||||
err = 1;
|
||||
fprintf(stderr, "%s: unrecognized option '%s'\n", appname, p);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
struct uxsel_id {
|
||||
#if GTK_CHECK_VERSION(2,0,0)
|
||||
GIOChannel *chan;
|
||||
guint watch_id;
|
||||
#else
|
||||
int id;
|
||||
#endif
|
||||
};
|
||||
|
||||
uxsel_id *uxsel_input_add(int fd, int rwx) {
|
||||
uxsel_id *id = snew(uxsel_id);
|
||||
|
||||
#if GTK_CHECK_VERSION(2,0,0)
|
||||
int flags = 0;
|
||||
if (rwx & 1) flags |= G_IO_IN;
|
||||
if (rwx & 2) flags |= G_IO_OUT;
|
||||
if (rwx & 4) flags |= G_IO_PRI;
|
||||
id->chan = g_io_channel_unix_new(fd);
|
||||
g_io_channel_set_encoding(id->chan, NULL, NULL);
|
||||
id->watch_id = g_io_add_watch_full(id->chan, GDK_PRIORITY_REDRAW+1, flags,
|
||||
fd_input_func, NULL, NULL);
|
||||
#else
|
||||
int flags = 0;
|
||||
if (rwx & 1) flags |= GDK_INPUT_READ;
|
||||
if (rwx & 2) flags |= GDK_INPUT_WRITE;
|
||||
if (rwx & 4) flags |= GDK_INPUT_EXCEPTION;
|
||||
assert(flags);
|
||||
id->id = gdk_input_add(fd, flags, fd_input_func, NULL);
|
||||
#endif
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void uxsel_input_remove(uxsel_id *id) {
|
||||
#if GTK_CHECK_VERSION(2,0,0)
|
||||
g_source_remove(id->watch_id);
|
||||
g_io_channel_unref(id->chan);
|
||||
#else
|
||||
gdk_input_remove(id->id);
|
||||
#endif
|
||||
sfree(id);
|
||||
}
|
||||
|
||||
int frontend_is_utf8(void *frontend)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)frontend;
|
||||
|
@ -4408,200 +3840,16 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
|
|||
inst->reconfiguring = FALSE;
|
||||
}
|
||||
|
||||
void fork_and_exec_self(struct gui_data *inst, int fd_to_close, ...)
|
||||
{
|
||||
/*
|
||||
* Re-execing ourself is not an exact science under Unix. I do
|
||||
* the best I can by using /proc/self/exe if available and by
|
||||
* assuming argv[0] can be found on $PATH if not.
|
||||
*
|
||||
* Note that we also have to reconstruct the elements of the
|
||||
* original argv which gtk swallowed, since the user wants the
|
||||
* new session to appear on the same X display as the old one.
|
||||
*/
|
||||
char **args;
|
||||
va_list ap;
|
||||
int i, n;
|
||||
int pid;
|
||||
|
||||
/*
|
||||
* Collect the arguments with which to re-exec ourself.
|
||||
*/
|
||||
va_start(ap, fd_to_close);
|
||||
n = 2; /* progname and terminating NULL */
|
||||
n += inst->ngtkargs;
|
||||
while (va_arg(ap, char *) != NULL)
|
||||
n++;
|
||||
va_end(ap);
|
||||
|
||||
args = snewn(n, char *);
|
||||
args[0] = inst->progname;
|
||||
args[n-1] = NULL;
|
||||
for (i = 0; i < inst->ngtkargs; i++)
|
||||
args[i+1] = inst->gtkargvstart[i];
|
||||
|
||||
i++;
|
||||
va_start(ap, fd_to_close);
|
||||
while ((args[i++] = va_arg(ap, char *)) != NULL);
|
||||
va_end(ap);
|
||||
|
||||
assert(i == n);
|
||||
|
||||
/*
|
||||
* Do the double fork.
|
||||
*/
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
perror("fork");
|
||||
sfree(args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
int pid2 = fork();
|
||||
if (pid2 < 0) {
|
||||
perror("fork");
|
||||
_exit(1);
|
||||
} else if (pid2 > 0) {
|
||||
/*
|
||||
* First child has successfully forked second child. My
|
||||
* Work Here Is Done. Note the use of _exit rather than
|
||||
* exit: the latter appears to cause destroy messages
|
||||
* to be sent to the X server. I suspect gtk uses
|
||||
* atexit.
|
||||
*/
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we reach here, we are the second child, so we now
|
||||
* actually perform the exec.
|
||||
*/
|
||||
if (fd_to_close >= 0)
|
||||
close(fd_to_close);
|
||||
|
||||
execv("/proc/self/exe", args);
|
||||
execvp(inst->progname, args);
|
||||
perror("exec");
|
||||
_exit(127);
|
||||
|
||||
} else {
|
||||
int status;
|
||||
sfree(args);
|
||||
waitpid(pid, &status, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void dup_session_menuitem(GtkMenuItem *item, gpointer gdata)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)gdata;
|
||||
/*
|
||||
* For this feature we must marshal conf and (possibly) pty_argv
|
||||
* into a byte stream, create a pipe, and send this byte stream
|
||||
* to the child through the pipe.
|
||||
*/
|
||||
int i, ret, sersize, size;
|
||||
char *data;
|
||||
char option[80];
|
||||
int pipefd[2];
|
||||
|
||||
if (pipe(pipefd) < 0) {
|
||||
perror("pipe");
|
||||
return;
|
||||
}
|
||||
|
||||
size = sersize = conf_serialised_size(inst->conf);
|
||||
if (use_pty_argv && pty_argv) {
|
||||
for (i = 0; pty_argv[i]; i++)
|
||||
size += strlen(pty_argv[i]) + 1;
|
||||
}
|
||||
|
||||
data = snewn(size, char);
|
||||
conf_serialise(inst->conf, data);
|
||||
if (use_pty_argv && pty_argv) {
|
||||
int p = sersize;
|
||||
for (i = 0; pty_argv[i]; i++) {
|
||||
strcpy(data + p, pty_argv[i]);
|
||||
p += strlen(pty_argv[i]) + 1;
|
||||
}
|
||||
assert(p == size);
|
||||
}
|
||||
|
||||
sprintf(option, "---[%d,%d]", pipefd[0], size);
|
||||
noncloexec(pipefd[0]);
|
||||
fork_and_exec_self(inst, pipefd[1], option, NULL);
|
||||
close(pipefd[0]);
|
||||
|
||||
i = ret = 0;
|
||||
while (i < size && (ret = write(pipefd[1], data + i, size - i)) > 0)
|
||||
i += ret;
|
||||
if (ret < 0)
|
||||
perror("write to pipe");
|
||||
close(pipefd[1]);
|
||||
sfree(data);
|
||||
}
|
||||
|
||||
int read_dupsession_data(struct gui_data *inst, Conf *conf, char *arg)
|
||||
{
|
||||
int fd, i, ret, size, size_used;
|
||||
char *data;
|
||||
|
||||
if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) {
|
||||
fprintf(stderr, "%s: malformed magic argument `%s'\n", appname, arg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
data = snewn(size, char);
|
||||
i = ret = 0;
|
||||
while (i < size && (ret = read(fd, data + i, size - i)) > 0)
|
||||
i += ret;
|
||||
if (ret < 0) {
|
||||
perror("read from pipe");
|
||||
exit(1);
|
||||
} else if (i < size) {
|
||||
fprintf(stderr, "%s: unexpected EOF in Duplicate Session data\n",
|
||||
appname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
size_used = conf_deserialise(conf, data, size);
|
||||
if (use_pty_argv && size > size_used) {
|
||||
int n = 0;
|
||||
i = size_used;
|
||||
while (i < size) {
|
||||
while (i < size && data[i]) i++;
|
||||
if (i >= size) {
|
||||
fprintf(stderr, "%s: malformed Duplicate Session data\n",
|
||||
appname);
|
||||
exit(1);
|
||||
}
|
||||
i++;
|
||||
n++;
|
||||
}
|
||||
pty_argv = snewn(n+1, char *);
|
||||
pty_argv[n] = NULL;
|
||||
n = 0;
|
||||
i = size_used;
|
||||
while (i < size) {
|
||||
char *p = data + i;
|
||||
while (i < size && data[i]) i++;
|
||||
assert(i < size);
|
||||
i++;
|
||||
pty_argv[n++] = dupstr(p);
|
||||
}
|
||||
}
|
||||
|
||||
sfree(data);
|
||||
|
||||
return 0;
|
||||
launch_duplicate_session(inst->conf);
|
||||
}
|
||||
|
||||
void new_session_menuitem(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)data;
|
||||
|
||||
fork_and_exec_self(inst, -1, NULL);
|
||||
launch_new_session();
|
||||
}
|
||||
|
||||
void restart_session_menuitem(GtkMenuItem *item, gpointer data)
|
||||
|
@ -4618,10 +3866,9 @@ void restart_session_menuitem(GtkMenuItem *item, gpointer data)
|
|||
|
||||
void saved_session_menuitem(GtkMenuItem *item, gpointer data)
|
||||
{
|
||||
struct gui_data *inst = (struct gui_data *)data;
|
||||
char *str = (char *)g_object_get_data(G_OBJECT(item), "user-data");
|
||||
|
||||
fork_and_exec_self(inst, -1, "-load", str, NULL);
|
||||
launch_saved_session(str);
|
||||
}
|
||||
|
||||
void saved_session_freedata(GtkMenuItem *item, gpointer data)
|
||||
|
@ -4814,13 +4061,10 @@ static void start_backend(struct gui_data *inst)
|
|||
gtk_widget_set_sensitive(inst->restartitem, FALSE);
|
||||
}
|
||||
|
||||
int pt_main(int argc, char **argv)
|
||||
struct gui_data *new_session_window(Conf *conf, const char *geometry_string)
|
||||
{
|
||||
extern int cfgbox(Conf *conf);
|
||||
struct gui_data *inst;
|
||||
|
||||
setlocale(LC_CTYPE, "");
|
||||
|
||||
/*
|
||||
* Create an instance structure and initialise to zeroes
|
||||
*/
|
||||
|
@ -4828,57 +4072,34 @@ int pt_main(int argc, char **argv)
|
|||
memset(inst, 0, sizeof(*inst));
|
||||
inst->alt_keycode = -1; /* this one needs _not_ to be zero */
|
||||
inst->busy_status = BUSY_NOT;
|
||||
inst->conf = conf_new();
|
||||
inst->conf = conf;
|
||||
inst->wintitle = inst->icontitle = NULL;
|
||||
inst->quit_fn_scheduled = FALSE;
|
||||
inst->idle_fn_scheduled = FALSE;
|
||||
inst->drawtype = DRAWTYPE_DEFAULT;
|
||||
#if GTK_CHECK_VERSION(3,4,0)
|
||||
inst->cumulative_scroll = 0.0;
|
||||
#endif
|
||||
|
||||
/* defer any child exit handling until we're ready to deal with
|
||||
* it */
|
||||
block_signal(SIGCHLD, 1);
|
||||
if (geometry_string) {
|
||||
#if GTK_CHECK_VERSION(2,0,0)
|
||||
inst->geometry = geometry_string;
|
||||
#else
|
||||
/* On GTK 1, we have to do this using raw Xlib */
|
||||
int flags, x, y;
|
||||
unsigned int w, h;
|
||||
flags = XParseGeometry(geometry_string, &x, &y, &w, &h);
|
||||
if (flags & WidthValue)
|
||||
conf_set_int(conf, CONF_width, w);
|
||||
if (flags & HeightValue)
|
||||
conf_set_int(conf, CONF_height, h);
|
||||
|
||||
inst->progname = argv[0];
|
||||
/*
|
||||
* Copy the original argv before letting gtk_init fiddle with
|
||||
* it. It will be required later.
|
||||
*/
|
||||
{
|
||||
int i, oldargc;
|
||||
inst->gtkargvstart = snewn(argc-1, char *);
|
||||
for (i = 1; i < argc; i++)
|
||||
inst->gtkargvstart[i-1] = dupstr(argv[i]);
|
||||
oldargc = argc;
|
||||
gtk_init(&argc, &argv);
|
||||
inst->ngtkargs = oldargc - argc;
|
||||
}
|
||||
|
||||
if (argc > 1 && !strncmp(argv[1], "---", 3)) {
|
||||
read_dupsession_data(inst, inst->conf, argv[1]);
|
||||
/* Splatter this argument so it doesn't clutter a ps listing */
|
||||
smemclr(argv[1], strlen(argv[1]));
|
||||
} else {
|
||||
/* By default, we bring up the config dialog, rather than launching
|
||||
* a session. This gets set to TRUE if something happens to change
|
||||
* that (e.g., a hostname is specified on the command-line). */
|
||||
int allow_launch = FALSE;
|
||||
if (do_cmdline(argc, argv, 0, &allow_launch, inst, inst->conf))
|
||||
exit(1); /* pre-defaults pass to get -class */
|
||||
do_defaults(NULL, inst->conf);
|
||||
if (do_cmdline(argc, argv, 1, &allow_launch, inst, inst->conf))
|
||||
exit(1); /* post-defaults, do everything */
|
||||
|
||||
cmdline_run_saved(inst->conf);
|
||||
|
||||
if (loaded_session)
|
||||
allow_launch = TRUE;
|
||||
|
||||
if ((!allow_launch || !conf_launchable(inst->conf)) &&
|
||||
!cfgbox(inst->conf))
|
||||
exit(0); /* config box hit Cancel */
|
||||
if (flags & (XValue | YValue)) {
|
||||
inst->xpos = x;
|
||||
inst->ypos = y;
|
||||
inst->gotpos = TRUE;
|
||||
inst->gravity = ((flags & XNegative ? 1 : 0) |
|
||||
(flags & YNegative ? 2 : 0));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!compound_text_atom)
|
||||
|
@ -5112,14 +4333,10 @@ int pt_main(int argc, char **argv)
|
|||
|
||||
inst->eventlogstuff = eventlogstuff_new();
|
||||
|
||||
request_callback_notifications(notify_toplevel_callback, inst);
|
||||
|
||||
inst->term = term_init(inst->conf, &inst->ucsdata, inst);
|
||||
inst->logctx = log_init(inst, inst->conf);
|
||||
term_provide_logctx(inst->term, inst->logctx);
|
||||
|
||||
uxsel_init();
|
||||
|
||||
term_size(inst->term, inst->height, inst->width,
|
||||
conf_get_int(inst->conf, CONF_savelines));
|
||||
|
||||
|
@ -5127,22 +4344,7 @@ int pt_main(int argc, char **argv)
|
|||
|
||||
ldisc_echoedit_update(inst->ldisc); /* cause ldisc to notice changes */
|
||||
|
||||
/* now we're reday to deal with the child exit handler being
|
||||
* called */
|
||||
block_signal(SIGCHLD, 0);
|
||||
|
||||
/*
|
||||
* Block SIGPIPE: if we attempt Duplicate Session or similar
|
||||
* and it falls over in some way, we certainly don't want
|
||||
* SIGPIPE terminating the main pterm/PuTTY. Note that we do
|
||||
* this _after_ (at least pterm) forks off its child process,
|
||||
* since the child wants SIGPIPE handled in the usual way.
|
||||
*/
|
||||
block_signal(SIGPIPE, 1);
|
||||
|
||||
inst->exited = FALSE;
|
||||
|
||||
gtk_main();
|
||||
|
||||
return 0;
|
||||
return inst;
|
||||
}
|
||||
|
|
12
unix/unix.h
12
unix/unix.h
|
@ -94,6 +94,18 @@ unsigned long getticks(void); /* based on gettimeofday(2) */
|
|||
*/
|
||||
#define FLAG_STDERR_TTY 0x1000
|
||||
|
||||
/* The per-session frontend structure managed by gtkwin.c */
|
||||
struct gui_data;
|
||||
struct gui_data *new_session_window(Conf *conf, const char *geometry_string);
|
||||
|
||||
/* Defined in gtkmain.c */
|
||||
void launch_duplicate_session(Conf *conf);
|
||||
void launch_new_session(void);
|
||||
void launch_saved_session(const char *str);
|
||||
|
||||
/* Defined in gtkcomm.c */
|
||||
void gtkcomm_setup(void);
|
||||
|
||||
/* Things pty.c needs from pterm.c */
|
||||
const char *get_x_display(void *frontend);
|
||||
int font_dimension(void *frontend, int which);/* 0 for width, 1 for height */
|
||||
|
|
|
@ -912,14 +912,14 @@ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf,
|
|||
/*
|
||||
* SIGINT, SIGQUIT and SIGPIPE may have been set to ignored by
|
||||
* our parent, particularly by things like sh -c 'pterm &' and
|
||||
* some window or session managers. SIGCHLD, meanwhile, was
|
||||
* blocked during pt_main() startup. Reverse all this for our
|
||||
* child process.
|
||||
* some window or session managers. SIGPIPE was also
|
||||
* (potentially) blocked by us during startup. Reverse all
|
||||
* this for our child process.
|
||||
*/
|
||||
putty_signal(SIGINT, SIG_DFL);
|
||||
putty_signal(SIGQUIT, SIG_DFL);
|
||||
putty_signal(SIGPIPE, SIG_DFL);
|
||||
block_signal(SIGCHLD, 0);
|
||||
block_signal(SIGPIPE, 0);
|
||||
if (pty_argv) {
|
||||
/*
|
||||
* Exec the exact argument list we were given.
|
||||
|
|
Загрузка…
Ссылка в новой задаче