]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/gtkapp.c
Unix buildinfo: stop saying 'GTK' in pure CLI utilities.
[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 const int buildinfo_gtk_relevant = TRUE;
131
132 #if !GTK_CHECK_VERSION(3,0,0)
133 /* This front end only works in GTK 3. If that's not what we've got,
134  * it's easier to just turn this program into a trivial stub by ifdef
135  * in the source than it is to remove it in the makefile edifice. */
136 int main(int argc, char **argv)
137 {
138     fprintf(stderr, "launcher does nothing on non-OSX platforms\n");
139     return 1;
140 }
141 GtkWidget *make_gtk_toplevel_window(void *frontend) { return NULL; }
142 void launch_duplicate_session(Conf *conf) {}
143 void launch_new_session(void) {}
144 void launch_saved_session(const char *str) {}
145 #else /* GTK_CHECK_VERSION(3,0,0) */
146
147 static void startup(GApplication *app, gpointer user_data)
148 {
149     GMenu *menubar, *menu, *section;
150
151     menubar = g_menu_new();
152
153     menu = g_menu_new();
154     g_menu_append_submenu(menubar, "File", G_MENU_MODEL(menu));
155
156     section = g_menu_new();
157     g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
158     g_menu_append(section, "New Window", "app.newwin");
159
160     menu = g_menu_new();
161     g_menu_append_submenu(menubar, "Edit", G_MENU_MODEL(menu));
162
163     section = g_menu_new();
164     g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
165     g_menu_append(section, "Paste", "win.paste");
166
167     gtk_application_set_menubar(GTK_APPLICATION(app),
168                                 G_MENU_MODEL(menubar));
169 }
170
171 static void paste_cb(GSimpleAction *action,
172                      GVariant      *parameter,
173                      gpointer       user_data)
174 {
175     request_paste(user_data);
176 }
177
178 static const GActionEntry win_actions[] = {
179     { "paste", paste_cb },
180 };
181
182 static GtkApplication *app;
183 GtkWidget *make_gtk_toplevel_window(void *frontend)
184 {
185     GtkWidget *win = gtk_application_window_new(app);
186     g_action_map_add_action_entries(G_ACTION_MAP(win),
187                                     win_actions,
188                                     G_N_ELEMENTS(win_actions),
189                                     frontend);
190     return win;
191 }
192
193 extern int cfgbox(Conf *conf);
194
195 void launch_duplicate_session(Conf *conf)
196 {
197     extern const int dup_check_launchable;
198     assert(!dup_check_launchable || conf_launchable(conf));
199     new_session_window(conf, NULL);
200 }
201
202 void launch_new_session(void)
203 {
204     Conf *conf = conf_new();
205     do_defaults(NULL, conf);
206     if (conf_launchable(conf) || cfgbox(conf)) {
207         new_session_window(conf, NULL);
208     }
209 }
210
211 void launch_saved_session(const char *str)
212 {
213     Conf *conf = conf_new();
214     do_defaults(str, conf);
215     if (conf_launchable(conf) || cfgbox(conf)) {
216         new_session_window(conf, NULL);
217     }
218 }
219
220 void new_app_win(GtkApplication *app)
221 {
222     launch_new_session();
223 }
224
225 static void activate(GApplication *app,
226                      gpointer      user_data)
227 {
228     new_app_win(GTK_APPLICATION(app));
229 }
230
231 static void newwin_cb(GSimpleAction *action,
232                       GVariant      *parameter,
233                       gpointer       user_data)
234 {
235     new_app_win(GTK_APPLICATION(user_data));
236 }
237
238 static void quit_cb(GSimpleAction *action,
239                     GVariant      *parameter,
240                     gpointer       user_data)
241 {
242     g_application_quit(G_APPLICATION(user_data));
243 }
244
245 static const GActionEntry app_actions[] = {
246     { "newwin", newwin_cb },
247     { "quit", quit_cb },
248 };
249
250 int main(int argc, char **argv)
251 {
252     int status;
253
254     {
255         /* Call the function in ux{putty,pterm}.c to do app-type
256          * specific setup */
257         extern void setup(int);
258         setup(FALSE);     /* FALSE means we are not a one-session process */
259     }
260
261     if (argc > 1) {
262         extern char *pty_osx_envrestore_prefix;
263         pty_osx_envrestore_prefix = argv[--argc];
264     }
265
266     {
267         const char *home = getenv("HOME");
268         if (home) {
269             if (chdir(home)) {}
270         }
271     }
272
273     gtkcomm_setup();
274
275     app = gtk_application_new("org.tartarus.projects.putty.macputty",
276                               G_APPLICATION_FLAGS_NONE);
277     g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
278     g_signal_connect(app, "startup", G_CALLBACK(startup), NULL);
279     g_action_map_add_action_entries(G_ACTION_MAP(app),
280                                     app_actions,
281                                     G_N_ELEMENTS(app_actions),
282                                     app);
283
284     status = g_application_run(G_APPLICATION(app), argc, argv);
285     g_object_unref(app);
286
287     return status;
288 }
289
290 #endif /* GTK_CHECK_VERSION(3,0,0) */