]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/gtkapp.c
Cross-reference all the host key docs.
[PuTTY.git] / unix / gtkapp.c
1 /*
2  * gtkapp.c: a top-level front end to GUI PuTTY and pterm, using
3  * GtkApplication. Suitable for OS X. Currently unfinished.
4  *
5  * (You could run it on ordinary Linux GTK too, in principle, but I
6  * don't think it would be particularly useful to do so, even once
7  * it's fully working.)
8  */
9
10 /*
11
12 To build on OS X, you will need a build environment with GTK 3 and
13 gtk-mac-bundler, and also Halibut on the path (to build the man pages,
14 without which the standard Makefile will complain). Then, from a clean
15 checkout, do this:
16
17 ./mkfiles.pl -U --with-quartz
18 make -C icons icns
19 make -C doc
20 make
21
22 and you should get unix/PuTTY.app and unix/PTerm.app as output.
23
24 */
25
26 /*
27
28 TODO list for a sensible GTK3 PuTTY/pterm on OS X:
29
30 Menu items' keyboard shortcuts (Command-Q for Quit, Command-V for
31 Paste) do not currently work. It's intentional that if you turn on
32 'Command key acts as Meta' in the configuration then those shortcuts
33 should be superseded by the Meta-key functionality (e.g. Cmd-Q should
34 send ESC Q to the session), for the benefit of people whose non-Mac
35 keyboard reflexes expect the Meta key to be in that position; but if
36 you don't turn that option on, then these shortcuts should work as an
37 ordinary Mac user expects, and currently they don't.
38
39 Windows don't close sensibly when their sessions terminate. This is
40 because until now I've relied on calling cleanup_exit() or
41 gtk_main_quit() in gtkwin.c to terminate the program, which is
42 conceptually wrong in this situation (we don't want to quit the whole
43 application when just one window closes) and also doesn't reliably
44 work anyway (GtkApplication doesn't seem to have a gtk_main invocation
45 in it at all, so those calls to gtk_main_quit produce a GTK assertion
46 failure message on standard error). Need to introduce a proper 'clean
47 up this struct gui_data' function (including finalising other stuff
48 dangling off it like the backend), call that, and delete just that one
49 window. (And then work out a replacement mechanism for having the
50 ordinary Unix-style gtkmain.c based programs terminate when their
51 session does.) connection_fatal() in particular should invoke this
52 mechanism, and terminate just the connection that had trouble.
53
54 Mouse wheel events and trackpad scrolling gestures don't work quite
55 right in the terminal drawing area.
56
57 There doesn't seem to be a resize handle on terminal windows. I don't
58 think this is a fundamental limitation of OS X GTK (their demo app has
59 one), so perhaps I need to do something to make sure it appears?
60
61 A slight oddity with menus that pop up directly under the mouse
62 pointer: mousing over the menu items doesn't highlight them initially,
63 but if I mouse off the menu and back on (without un-popping-it-up)
64 then suddenly that does work. I don't know if this is something I can
65 fix, though; it might very well be a quirk of the underlying GTK.
66
67 I want to arrange *some* way to paste efficiently using my Apple
68 wireless keyboard and trackpad. The trackpad doesn't provide a middle
69 button; I can't use the historic Shift-Ins shortcut because the
70 keyboard has no Ins key; I configure the Command key to be Meta, so
71 Command-V is off the table too. I can always use the menu, but I'd
72 prefer there to be _some_ easily reachable mouse or keyboard gesture.
73
74 Revamping the clipboard handling in general is going to be needed, as
75 well. Not everybody will want the current auto-copy-on-select
76 behaviour inherited from ordinary Unix PuTTY. Should arrange to have a
77 mode in which you have to take an explicit Copy action, and then
78 arrange that the Edit menu includes one of those.
79
80 Dialog boxes shouldn't be modal. I think this is a good policy change
81 in general, and the required infrastructure changes will benefit the
82 Windows front end too, but for a multi-session process it's even more
83 critical - you need to be able to type into one session window while
84 setting up the configuration for launching another. So everywhere we
85 currently run a sub-instance of gtk_main, or call any API function
86 that implicitly does that (like gtk_dialog_run), we should switch to
87 putting up the dialog box and going back to our ordinary main loop,
88 and whatever we were going to do after the dialog closed we should
89 remember to do it when that happens later on. Also then we can remove
90 the post_main() horror from gtkcomm.c.
91
92 The application menu bar is very minimal at the moment. Should include
93 all the usual stuff from the Ctrl-right-click menu - saved sessions,
94 mid-session special commands, Duplicate Session, Change Settings,
95 Event Log, clear scrollback, reset terminal, about box, anything else
96 I can think of.
97
98 Does OS X have a standard system of online help that I could tie into?
99
100 Need to work out what if anything we can do with Pageant on OS X.
101 Perhaps it's too much bother and we should just talk to the
102 system-provided SSH agent? Or perhaps not.
103
104 Nice-to-have: a custom right-click menu from the application's dock
105 tile, listing the saved sessions for quick launch. As far as I know
106 there's nothing built in to GtkApplication that can produce this, but
107 it's possible we might be able to drop a piece of native Cocoa code in
108 under ifdef, substituting an application delegate of our own which
109 forwards all methods we're not interested in to the GTK-provided one?
110
111 At the point where this becomes polished enough to publish pre-built,
112 I suppose I'll have to look into OS X code signing.
113 https://wiki.gnome.org/Projects/GTK%2B/OSX/Bundling has some links.
114
115  */
116
117 #include <assert.h>
118 #include <stdlib.h>
119
120 #include <unistd.h>
121
122 #include <gtk/gtk.h>
123
124 #define MAY_REFER_TO_GTK_IN_HEADERS
125
126 #include "putty.h"
127
128 char *x_get_default(const char *key) { return NULL; }
129
130 #if !GTK_CHECK_VERSION(3,0,0)
131 /* This front end only works in GTK 3. If that's not what we've got,
132  * it's easier to just turn this program into a trivial stub by ifdef
133  * in the source than it is to remove it in the makefile edifice. */
134 int main(int argc, char **argv)
135 {
136     fprintf(stderr, "launcher does nothing on non-OSX platforms\n");
137     return 1;
138 }
139 GtkWidget *make_gtk_toplevel_window(void *frontend) { return NULL; }
140 void launch_duplicate_session(Conf *conf) {}
141 void launch_new_session(void) {}
142 void launch_saved_session(const char *str) {}
143 #else /* GTK_CHECK_VERSION(3,0,0) */
144
145 static void startup(GApplication *app, gpointer user_data)
146 {
147     GMenu *menubar, *menu, *section;
148
149     menubar = g_menu_new();
150
151     menu = g_menu_new();
152     g_menu_append_submenu(menubar, "File", G_MENU_MODEL(menu));
153
154     section = g_menu_new();
155     g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
156     g_menu_append(section, "New Window", "app.newwin");
157
158     menu = g_menu_new();
159     g_menu_append_submenu(menubar, "Edit", G_MENU_MODEL(menu));
160
161     section = g_menu_new();
162     g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
163     g_menu_append(section, "Paste", "win.paste");
164
165     gtk_application_set_menubar(GTK_APPLICATION(app),
166                                 G_MENU_MODEL(menubar));
167 }
168
169 static void paste_cb(GSimpleAction *action,
170                      GVariant      *parameter,
171                      gpointer       user_data)
172 {
173     request_paste(user_data);
174 }
175
176 static const GActionEntry win_actions[] = {
177     { "paste", paste_cb },
178 };
179
180 static GtkApplication *app;
181 GtkWidget *make_gtk_toplevel_window(void *frontend)
182 {
183     GtkWidget *win = gtk_application_window_new(app);
184     g_action_map_add_action_entries(G_ACTION_MAP(win),
185                                     win_actions,
186                                     G_N_ELEMENTS(win_actions),
187                                     frontend);
188     return win;
189 }
190
191 extern int cfgbox(Conf *conf);
192
193 void launch_duplicate_session(Conf *conf)
194 {
195     assert(conf_launchable(conf));
196     new_session_window(conf, NULL);
197 }
198
199 void launch_new_session(void)
200 {
201     Conf *conf = conf_new();
202     do_defaults(NULL, conf);
203     if (conf_launchable(conf) || cfgbox(conf)) {
204         new_session_window(conf, NULL);
205     }
206 }
207
208 void launch_saved_session(const char *str)
209 {
210     Conf *conf = conf_new();
211     do_defaults(str, conf);
212     if (conf_launchable(conf) || cfgbox(conf)) {
213         new_session_window(conf, NULL);
214     }
215 }
216
217 void new_app_win(GtkApplication *app)
218 {
219     launch_new_session();
220 }
221
222 static void activate(GApplication *app,
223                      gpointer      user_data)
224 {
225     new_app_win(GTK_APPLICATION(app));
226 }
227
228 static void newwin_cb(GSimpleAction *action,
229                       GVariant      *parameter,
230                       gpointer       user_data)
231 {
232     new_app_win(GTK_APPLICATION(user_data));
233 }
234
235 static void quit_cb(GSimpleAction *action,
236                     GVariant      *parameter,
237                     gpointer       user_data)
238 {
239     g_application_quit(G_APPLICATION(user_data));
240 }
241
242 static const GActionEntry app_actions[] = {
243     { "newwin", newwin_cb },
244     { "quit", quit_cb },
245 };
246
247 int main(int argc, char **argv)
248 {
249     int status;
250
251     {
252         /* Call the function in ux{putty,pterm}.c to do app-type
253          * specific setup */
254         extern void setup(int);
255         setup(FALSE);     /* FALSE means we are not a one-session process */
256     }
257
258     if (argc > 1) {
259         extern char *pty_osx_envrestore_prefix;
260         pty_osx_envrestore_prefix = argv[--argc];
261     }
262
263     {
264         const char *home = getenv("HOME");
265         if (home) {
266             if (chdir(home)) {}
267         }
268     }
269
270     gtkcomm_setup();
271
272     app = gtk_application_new("org.tartarus.projects.putty.macputty",
273                               G_APPLICATION_FLAGS_NONE);
274     g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
275     g_signal_connect(app, "startup", G_CALLBACK(startup), NULL);
276     g_action_map_add_action_entries(G_ACTION_MAP(app),
277                                     app_actions,
278                                     G_N_ELEMENTS(app_actions),
279                                     app);
280
281     status = g_application_run(G_APPLICATION(app), argc, argv);
282     g_object_unref(app);
283
284     return status;
285 }
286
287 #endif /* GTK_CHECK_VERSION(3,0,0) */