First phase of porting. pterm now compiles and runs under Linux+gtk.
[u/mdw/putty] / unix / pterm.c
1 /*
2 * pterm - a fusion of the PuTTY terminal emulator with a Unix pty
3 * back end, all running as a GTK application. Wish me luck.
4 */
5
6 #include <string.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <time.h>
10 #include <gtk/gtk.h>
11
12 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
13 #include "putty.h"
14
15 #define CAT2(x,y) x ## y
16 #define CAT(x,y) CAT2(x,y)
17 #define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)}
18
19 void ldisc_update(int echo, int edit)
20 {
21 /*
22 * This is a stub in pterm. If I ever produce a Unix
23 * command-line ssh/telnet/rlogin client (i.e. a port of plink)
24 * then it will require some termios manoeuvring analogous to
25 * that in the Windows plink.c, but here it's meaningless.
26 */
27 }
28
29 int askappend(char *filename)
30 {
31 /*
32 * FIXME: for the moment we just wipe the log file. Since I
33 * haven't yet enabled logging, this shouldn't matter yet!
34 */
35 return 2;
36 }
37
38 void logevent(char *string)
39 {
40 /*
41 * FIXME: event log entries are currently ignored.
42 */
43 }
44
45 /*
46 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
47 * into a cooked one (SELECT, EXTEND, PASTE).
48 *
49 * In Unix, this is not configurable; the X button arrangement is
50 * rock-solid across all applications, everyone has a three-button
51 * mouse or a means of faking it, and there is no need to switch
52 * buttons around at all.
53 */
54 Mouse_Button translate_button(Mouse_Button button)
55 {
56 if (button == MBT_LEFT)
57 return MBT_SELECT;
58 if (button == MBT_MIDDLE)
59 return MBT_PASTE;
60 if (button == MBT_RIGHT)
61 return MBT_EXTEND;
62 return 0; /* shouldn't happen */
63 }
64
65 /*
66 * Minimise or restore the window in response to a server-side
67 * request.
68 */
69 void set_iconic(int iconic)
70 {
71 /* FIXME: currently ignored */
72 }
73
74 /*
75 * Move the window in response to a server-side request.
76 */
77 void move_window(int x, int y)
78 {
79 /* FIXME: currently ignored */
80 }
81
82 /*
83 * Move the window to the top or bottom of the z-order in response
84 * to a server-side request.
85 */
86 void set_zorder(int top)
87 {
88 /* FIXME: currently ignored */
89 }
90
91 /*
92 * Refresh the window in response to a server-side request.
93 */
94 void refresh_window(void)
95 {
96 /* FIXME: currently ignored */
97 }
98
99 /*
100 * Maximise or restore the window in response to a server-side
101 * request.
102 */
103 void set_zoomed(int zoomed)
104 {
105 /* FIXME: currently ignored */
106 }
107
108 /*
109 * Report whether the window is iconic, for terminal reports.
110 */
111 int is_iconic(void)
112 {
113 return 0; /* FIXME */
114 }
115
116 /*
117 * Report the window's position, for terminal reports.
118 */
119 void get_window_pos(int *x, int *y)
120 {
121 *x = 3; *y = 4; /* FIXME */
122 }
123
124 /*
125 * Report the window's pixel size, for terminal reports.
126 */
127 void get_window_pixels(int *x, int *y)
128 {
129 *x = 1; *y = 2; /* FIXME */
130 }
131
132 /*
133 * Return the window or icon title.
134 */
135 char *get_window_title(int icon)
136 {
137 return "FIXME: window title retrieval not yet implemented";
138 }
139
140 struct gui_data {
141 GtkWidget *area;
142 GdkFont *fonts[2]; /* normal and bold (for now!) */
143 GdkGC *black_gc, *white_gc;
144 };
145
146 static struct gui_data the_inst;
147 static struct gui_data *inst = &the_inst; /* so we always write `inst->' */
148
149 gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data)
150 {
151 /*
152 * FIXME: warn on close?
153 */
154 return FALSE;
155 }
156
157 gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
158 {
159 struct gui_data *inst = (struct gui_data *)data;
160
161 inst->fonts[0] = gdk_font_load("9x15t"); /* XXCONFIG */
162 inst->fonts[1] = NULL; /* XXCONFIG */
163 inst->black_gc = widget->style->black_gc;
164 inst->white_gc = widget->style->white_gc;
165
166 #if 0 /* FIXME: get cmap from settings */
167 /*
168 * Set up the colour map.
169 */
170 inst->colmap = gdk_colormap_get_system();
171 {
172 char *colours[] = {
173 "#cc0000", "#880000", "#ff0000",
174 "#cc6600", "#884400", "#ff7f00",
175 "#cc9900", "#886600", "#ffbf00",
176 "#cccc00", "#888800", "#ffff00",
177 "#00cc00", "#008800", "#00ff00",
178 "#008400", "#005800", "#00b000",
179 "#008484", "#005858", "#00b0b0",
180 "#00cccc", "#008888", "#00ffff",
181 "#0066cc", "#004488", "#007fff",
182 "#9900cc", "#660088", "#bf00ff",
183 "#cc00cc", "#880088", "#ff00ff",
184 "#cc9999", "#886666", "#ffbfbf",
185 "#cccc99", "#888866", "#ffffbf",
186 "#99cc99", "#668866", "#bfffbf",
187 "#9999cc", "#666688", "#bfbfff",
188 "#757575", "#4e4e4e", "#9c9c9c",
189 "#999999", "#666666", "#bfbfbf",
190 "#cccccc", "#888888", "#ffffff",
191 };
192 ASSERT(sizeof(colours)/sizeof(*colours)==3*NCOLOURS);
193 gboolean success[3*NCOLOURS];
194 int i;
195
196 for (i = 0; i < 3*NCOLOURS; i++) {
197 if (!gdk_color_parse(colours[i], &inst->cols[i]))
198 g_error("4tris: couldn't parse colour \"%s\"\n", colours[i]);
199 }
200
201 gdk_colormap_alloc_colors(inst->colmap, inst->cols, 3*NCOLOURS,
202 FALSE, FALSE, success);
203 for (i = 0; i < 3*NCOLOURS; i++) {
204 if (!success[i])
205 g_error("4tris: couldn't allocate colour \"%s\"\n", colours[i]);
206 }
207 }
208 #endif
209
210 return TRUE;
211 }
212
213 gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data)
214 {
215 /* struct gui_data *inst = (struct gui_data *)data; */
216
217 /*
218 * Pass the exposed rectangle to terminal.c, which will call us
219 * back to do the actual painting.
220 */
221 term_paint(NULL,
222 event->area.x / 9, event->area.y / 15,
223 (event->area.x + event->area.width - 1) / 9,
224 (event->area.y + event->area.height - 1) / 15);
225 return TRUE;
226 }
227
228 #define KEY_PRESSED(k) \
229 (inst->keystate[(k) / 32] & (1 << ((k) % 32)))
230
231 gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
232 {
233 /* struct gui_data *inst = (struct gui_data *)data; */
234
235 if (event->type == GDK_KEY_PRESS) {
236 char c[1];
237 c[0] = event->keyval;
238 ldisc_send(c, 1, 1);
239 term_out();
240 }
241
242 return TRUE;
243 }
244
245 gint timer_func(gpointer data)
246 {
247 /* struct gui_data *inst = (struct gui_data *)data; */
248
249 term_update();
250 return TRUE;
251 }
252
253 void destroy(GtkWidget *widget, gpointer data)
254 {
255 gtk_main_quit();
256 }
257
258 gint focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)
259 {
260 /*
261 * FIXME: need to faff with the cursor shape.
262 */
263 return FALSE;
264 }
265
266 /*
267 * set or clear the "raw mouse message" mode
268 */
269 void set_raw_mouse_mode(int activate)
270 {
271 /* FIXME: currently ignored */
272 }
273
274 void request_resize(int w, int h)
275 {
276 /* FIXME: currently ignored */
277 }
278
279 void palette_set(int n, int r, int g, int b)
280 {
281 /* FIXME: currently ignored */
282 }
283 void palette_reset(void)
284 {
285 /* FIXME: currently ignored */
286 }
287
288 void write_clip(wchar_t * data, int len, int must_deselect)
289 {
290 /* FIXME: currently ignored */
291 }
292
293 void get_clip(wchar_t ** p, int *len)
294 {
295 if (p) {
296 /* FIXME: currently nonfunctional */
297 *p = NULL;
298 *len = 0;
299 }
300 }
301
302 void set_title(char *title)
303 {
304 /* FIXME: currently ignored */
305 }
306
307 void set_icon(char *title)
308 {
309 /* FIXME: currently ignored */
310 }
311
312 void set_sbar(int total, int start, int page)
313 {
314 /* FIXME: currently ignored */
315 }
316
317 void sys_cursor(int x, int y)
318 {
319 /*
320 * This is meaningless under X.
321 */
322 }
323
324 void beep(int mode)
325 {
326 gdk_beep();
327 }
328
329 int CharWidth(Context ctx, int uc)
330 {
331 /*
332 * Under X, any fixed-width font really _is_ fixed-width.
333 * Double-width characters will be dealt with using a separate
334 * font. For the moment we can simply return 1.
335 */
336 return 1;
337 }
338
339 Context get_ctx(void)
340 {
341 GdkGC *gc = gdk_gc_new(inst->area->window);
342 return gc;
343 }
344
345 void free_ctx(Context ctx)
346 {
347 GdkGC *gc = (GdkGC *)ctx;
348 gdk_gc_unref(gc);
349 }
350
351 /*
352 * Draw a line of text in the window, at given character
353 * coordinates, in given attributes.
354 *
355 * We are allowed to fiddle with the contents of `text'.
356 */
357 void do_text(Context ctx, int x, int y, char *text, int len,
358 unsigned long attr, int lattr)
359 {
360 GdkColor fg, bg;
361
362 GdkGC *gc = (GdkGC *)ctx;
363 fg.red = fg.green = fg.blue = 65535;
364 bg.red = bg.green = bg.blue = 65535;
365 gdk_gc_set_foreground(gc, &fg);
366 gdk_gc_set_background(gc, &bg);
367
368 gdk_draw_text(inst->area->window, inst->fonts[0], inst->white_gc,
369 x*9, y*15 + inst->fonts[0]->ascent, text, len);
370 }
371
372 void do_cursor(Context ctx, int x, int y, char *text, int len,
373 unsigned long attr, int lattr)
374 {
375 /* FIXME: passive cursor NYI */
376 if (attr & TATTR_PASCURS) {
377 attr &= ~TATTR_PASCURS;
378 attr |= TATTR_ACTCURS;
379 }
380 do_text(ctx, x, y, text, len, attr, lattr);
381 }
382
383 void modalfatalbox(char *p, ...)
384 {
385 va_list ap;
386 fprintf(stderr, "FATAL ERROR: ");
387 va_start(ap, p);
388 vfprintf(stderr, p, ap);
389 va_end(ap);
390 fputc('\n', stderr);
391 exit(1);
392 }
393
394 int main(int argc, char **argv)
395 {
396 GtkWidget *window;
397
398 gtk_init(&argc, &argv);
399
400 do_defaults(NULL, &cfg);
401
402 init_ucs();
403
404 back = &pty_backend;
405 back->init(NULL, 0, NULL, 0);
406
407 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
408 inst->area = gtk_drawing_area_new();
409 gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area),
410 9*80, 15*24);/* FIXME: proper resizing stuff */
411
412 gtk_container_add(GTK_CONTAINER(window), inst->area);
413
414 gtk_signal_connect(GTK_OBJECT(window), "destroy",
415 GTK_SIGNAL_FUNC(destroy), inst);
416 gtk_signal_connect(GTK_OBJECT(window), "delete_event",
417 GTK_SIGNAL_FUNC(delete_window), inst);
418 gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
419 GTK_SIGNAL_FUNC(key_event), inst);
420 gtk_signal_connect(GTK_OBJECT(window), "key_release_event",
421 GTK_SIGNAL_FUNC(key_event), inst);
422 gtk_signal_connect(GTK_OBJECT(window), "focus_in_event",
423 GTK_SIGNAL_FUNC(focus_event), inst);
424 gtk_signal_connect(GTK_OBJECT(window), "focus_out_event",
425 GTK_SIGNAL_FUNC(focus_event), inst);
426 gtk_signal_connect(GTK_OBJECT(inst->area), "configure_event",
427 GTK_SIGNAL_FUNC(configure_area), inst);
428 gtk_signal_connect(GTK_OBJECT(inst->area), "expose_event",
429 GTK_SIGNAL_FUNC(expose_area), inst);
430 gtk_timeout_add(20, timer_func, inst);
431 gtk_widget_add_events(GTK_WIDGET(inst->area),
432 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
433
434 gtk_widget_show(inst->area);
435 gtk_widget_show(window);
436
437 term_init();
438 term_size(24, 80, 2000);
439
440 gtk_main();
441
442 return 0;
443 }