]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/pterm.c
First phase of porting. pterm now compiles and runs under Linux+gtk.
[PuTTY.git] / 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 }