First phase of porting. pterm now compiles and runs under Linux+gtk.
[u/mdw/putty] / unix / pterm.c
CommitLineData
f7f27309 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>
1709795f 8#include <stdio.h>
f7f27309 9#include <time.h>
10#include <gtk/gtk.h>
11
1709795f 12#define PUTTY_DO_GLOBALS /* actually _define_ globals */
13#include "putty.h"
14
f7f27309 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
1709795f 19void 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
29int 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
38void 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 */
54Mouse_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 */
69void set_iconic(int iconic)
70{
71 /* FIXME: currently ignored */
72}
73
74/*
75 * Move the window in response to a server-side request.
76 */
77void 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 */
86void set_zorder(int top)
87{
88 /* FIXME: currently ignored */
89}
90
91/*
92 * Refresh the window in response to a server-side request.
93 */
94void 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 */
103void set_zoomed(int zoomed)
104{
105 /* FIXME: currently ignored */
106}
107
108/*
109 * Report whether the window is iconic, for terminal reports.
110 */
111int is_iconic(void)
112{
113 return 0; /* FIXME */
114}
115
116/*
117 * Report the window's position, for terminal reports.
118 */
119void 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 */
127void 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 */
135char *get_window_title(int icon)
136{
137 return "FIXME: window title retrieval not yet implemented";
138}
f7f27309 139
140struct gui_data {
141 GtkWidget *area;
142 GdkFont *fonts[2]; /* normal and bold (for now!) */
143 GdkGC *black_gc, *white_gc;
144};
145
1709795f 146static struct gui_data the_inst;
147static struct gui_data *inst = &the_inst; /* so we always write `inst->' */
148
f7f27309 149gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data)
150{
151 /*
152 * FIXME: warn on close?
153 */
154 return FALSE;
155}
156
157gint 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
213gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data)
214{
1709795f 215 /* struct gui_data *inst = (struct gui_data *)data; */
f7f27309 216
217 /*
1709795f 218 * Pass the exposed rectangle to terminal.c, which will call us
219 * back to do the actual painting.
f7f27309 220 */
1709795f 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;
f7f27309 226}
227
228#define KEY_PRESSED(k) \
229 (inst->keystate[(k) / 32] & (1 << ((k) % 32)))
230
231gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
232{
1709795f 233 /* struct gui_data *inst = (struct gui_data *)data; */
f7f27309 234
1709795f 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;
f7f27309 243}
244
245gint timer_func(gpointer data)
246{
1709795f 247 /* struct gui_data *inst = (struct gui_data *)data; */
f7f27309 248
1709795f 249 term_update();
f7f27309 250 return TRUE;
251}
252
253void destroy(GtkWidget *widget, gpointer data)
254{
255 gtk_main_quit();
256}
257
258gint focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)
259{
260 /*
261 * FIXME: need to faff with the cursor shape.
262 */
1709795f 263 return FALSE;
264}
265
266/*
267 * set or clear the "raw mouse message" mode
268 */
269void set_raw_mouse_mode(int activate)
270{
271 /* FIXME: currently ignored */
272}
273
274void request_resize(int w, int h)
275{
276 /* FIXME: currently ignored */
277}
278
279void palette_set(int n, int r, int g, int b)
280{
281 /* FIXME: currently ignored */
282}
283void palette_reset(void)
284{
285 /* FIXME: currently ignored */
286}
287
288void write_clip(wchar_t * data, int len, int must_deselect)
289{
290 /* FIXME: currently ignored */
291}
292
293void get_clip(wchar_t ** p, int *len)
294{
295 if (p) {
296 /* FIXME: currently nonfunctional */
297 *p = NULL;
298 *len = 0;
299 }
300}
301
302void set_title(char *title)
303{
304 /* FIXME: currently ignored */
305}
306
307void set_icon(char *title)
308{
309 /* FIXME: currently ignored */
310}
311
312void set_sbar(int total, int start, int page)
313{
314 /* FIXME: currently ignored */
315}
316
317void sys_cursor(int x, int y)
318{
319 /*
320 * This is meaningless under X.
321 */
322}
323
324void beep(int mode)
325{
326 gdk_beep();
327}
328
329int 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
339Context get_ctx(void)
340{
341 GdkGC *gc = gdk_gc_new(inst->area->window);
342 return gc;
343}
344
345void 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 */
357void 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
372void 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
383void 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);
f7f27309 392}
393
394int main(int argc, char **argv)
395{
396 GtkWidget *window;
f7f27309 397
398 gtk_init(&argc, &argv);
399
1709795f 400 do_defaults(NULL, &cfg);
401
402 init_ucs();
403
404 back = &pty_backend;
405 back->init(NULL, 0, NULL, 0);
406
f7f27309 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
1709795f 437 term_init();
438 term_size(24, 80, 2000);
439
f7f27309 440 gtk_main();
441
442 return 0;
443}