checkin of 0.3.0
[pspp-builds.git] / sysdeps / windows / con32s.c
1 /* con32s - emulates Windows console.
2    Copyright (C) 1997, 1998 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <winbase.h>
21 #include <wingdi.h>
22 #include <winuser.h>
23 #include <stdio.h>
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27
28 typedef struct line_struct line;
29 struct line_struct
30   {
31     line *next, *prev;          /* next and previous lines */
32     char *text;                 /* text */
33     int len;                    /* number of characters in text */
34     int size;                   /* maximum allocated size for text */
35   };                            /* line */
36
37 /* Pointer to tail end of text lines. */
38 static line *tail;
39
40 /* Console window created. */
41 static int inited = 0;
42
43 /* Console window title. */
44 static const char *title = _("Con32s Console Emulator by Ben Pfaff");
45
46 /* Important variables. */
47 HINSTANCE _hInstance;
48 HINSTANCE _hPrev;
49 LPSTR _cmdline;
50 int _nCmdShow;
51
52 /* Console window. */
53 HWND wnd;
54
55 /* Width, height of a single character in the console font. */
56 int cw, ch;
57
58 /* Width, height of console window in characters. */
59 int nw, nh;
60
61 /* Keyboard buffer. */
62 #define MAX_KBD_BUF 80          /* Maximum number of characters to buffer. */
63 char kbd[MAX_KBD_BUF];
64 char *hp, *tp;                  /* Keyboard buffer head, tail. */
65
66 static void
67 outmsg (char *format,...)
68 {
69   va_list args;
70   char s[128];
71
72   va_start (args, format);
73   vsprintf (s, format, args);
74   va_end (args);
75   MessageBox (_hInstance, s, "Con32s",
76               MB_OK | MB_ICONHAND | MB_SYSTEMMODAL);
77 }
78
79 static void *
80 xmalloc (size_t size)
81 {
82   void *vp;
83   if (size == 0)
84     return NULL;
85   vp = malloc (size);
86   if (!vp)
87     {
88       MessageBox (NULL, _("xmalloc(): out of memory"), NULL, MB_OK);
89       exit (EXIT_FAILURE);
90     }
91   return vp;
92 }
93
94 static void *
95 xrealloc (void *ptr, size_t size)
96 {
97   void *vp;
98   if (!size)
99     {
100       if (ptr)
101         free (ptr);
102       return NULL;
103     }
104   if (ptr)
105     vp = realloc (ptr, size);
106   else
107     vp = malloc (size);
108   if (!vp)
109     {
110       MessageBox (NULL, _("xrealloc(): out of memory"), NULL, MB_OK);
111       exit (EXIT_FAILURE);
112     }
113   return vp;
114 }
115
116 void _blp_console_init (void);
117 void _blp_console_yield (void);
118 void _blp_console_paint (void);
119 void find_console_top (line ** top);
120 void find_console_bottom (int *x, int *y, line ** bottom);
121
122 static void
123 writechar (int c)
124 {
125   int x, y;
126   line *bottom;
127
128   static HDC dc;
129
130   if (c == 10000)
131     {
132       if (dc)
133         {
134           ReleaseDC (wnd, dc);
135           dc = 0;
136         }
137       return;
138     }
139
140   if (!tail)
141     {
142       tail = xmalloc (sizeof (line));
143       tail->next = tail->prev = NULL;
144       tail->text = NULL;
145       tail->len = tail->size = 0;
146     }
147
148   switch (c)
149     {
150     case '\n':
151       {
152         tail->next = xmalloc (sizeof (line));
153         tail->next->prev = tail;
154         tail = tail->next;
155         tail->next = NULL;
156         tail->text = NULL;
157         tail->len = tail->size = 0;
158       }
159       break;
160     case '\r':
161       break;
162     case '\b':
163       {
164         find_console_bottom (&x, &y, &bottom);
165         if (tail->len)
166           tail->len--;
167         else
168           {
169             tail = tail->prev;
170             free (tail->next);
171             tail->next = NULL;
172           }
173
174         if (x > 1)
175           {
176             if (!dc)
177               {
178                 dc = GetDC (wnd);
179                 SelectObject (dc, GetStockObject (ANSI_FIXED_FONT));
180                 assert (dc);
181               }
182             TextOut (dc, x * cw, y * ch, " ", 1);
183             return;
184           }
185       }
186       break;
187     default:
188       {
189         if (tail->len + 1 > tail->size)
190           {
191             tail->size += 16;
192             tail->text = xrealloc (tail->text, tail->size);
193           }
194
195         find_console_bottom (&x, &y, &bottom);
196         tail->text[tail->len++] = c;
197         if (y < nh)
198           {
199             if (!dc)
200               {
201                 dc = GetDC (wnd);
202                 SelectObject (dc, GetStockObject (ANSI_FIXED_FONT));
203                 assert (dc);
204               }
205             TextOut (dc, x * cw, y * ch, &tail->text[tail->len - 1], 1);
206             return;
207           }
208       }
209       break;
210     }
211   InvalidateRect (wnd, NULL, TRUE);
212 }
213
214 /* Writes LEN bytes from BUF to the fake console window. */
215 int
216 _blp_console_write (const void *buf, unsigned len)
217 {
218   int i;
219
220   if (!inited)
221     _blp_console_init ();
222   for (i = 0; i < len; i++)
223     writechar (((char *) buf)[i]);
224   writechar (10000);
225   return len;
226 }
227
228 /* Reads one character from the fake console window.  A whole line
229    is read at once, then spoon-fed to the runtime library. */
230 #if __BORLANDC__
231 #pragma argsused
232 #endif
233 int
234 _blp_console_read (const void *t1, unsigned t2)
235 {
236   static char buf[1024];
237   static int len;
238   static int n;
239
240   MSG msg;
241
242   int c;
243
244   if (!inited)
245     _blp_console_init ();
246   if (n < len)
247     {
248       *(char *) t1 = buf[n];
249       n++;
250       return 1;
251     }
252
253   printf ("_");
254   len = n = 0;
255   while (GetMessage ((LPMSG) & msg, NULL, 0, 0))
256     {
257       TranslateMessage ((LPMSG) & msg);
258       DispatchMessage ((LPMSG) & msg);
259
260       while (hp != tp)
261         {
262           c = *(unsigned char *) tp;
263           if (++tp >= &kbd[MAX_KBD_BUF])
264             tp = kbd;
265           if ((c >= 32 && c < 128) || c == '\b' || c == '\r')
266             switch (c)
267               {
268               case '\b':
269                 if (len <= 0)
270                   break;
271                 printf ("\b\b_");
272                 len--;
273                 break;
274               default:
275                 if (len >= 1022)
276                   break;
277                 if (c == '\r')
278                   {
279                     buf[len++] = '\n';
280                     printf ("\b\n");
281                     *(char *) t1 = buf[n];
282                     n++;
283                     return 1;
284                   }
285                 buf[len++] = c;
286                 printf ("\b%c_", c);
287                 break;
288               }
289         }
290     }
291   len = 0;
292   return 0;
293 }
294
295 LRESULT CALLBACK _export _blp_console_wndproc (HWND, UINT, WPARAM, LPARAM);
296
297 void
298 _blp_console_init (void)
299 {
300   WNDCLASS wc;
301
302   if (inited)
303     return;
304   inited = 1;
305   wc.style = CS_HREDRAW | CS_VREDRAW;
306   wc.lpfnWndProc = _blp_console_wndproc;
307   wc.cbClsExtra = 0;
308   wc.cbWndExtra = 0;
309   wc.hInstance = (HINSTANCE) _hInstance;
310   wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
311   wc.hCursor = LoadCursor (NULL, IDC_ARROW);
312   wc.hbrBackground = CreateSolidBrush (RGB (255, 255, 255));
313   wc.lpszMenuName = NULL;
314   wc.lpszClassName = "blp_console";
315   if (!RegisterClass (&wc))
316     {
317       MessageBox ((HWND) 0, _("RegisterClass(): returned 0."),
318                   "_blp_console_init()", MB_APPLMODAL | MB_OK);
319       exit (EXIT_FAILURE);
320     }
321
322   wnd = CreateWindow ("blp_console", title, WS_OVERLAPPEDWINDOW,
323                       CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
324                       CW_USEDEFAULT, NULL, (HMENU) 0, (HINSTANCE) _hInstance,
325                       NULL);
326   if (!wnd)
327     {
328       MessageBox ((HWND) 0, _("CreateWindow(): returned 0."),
329                   "_blp_console_init()", MB_APPLMODAL | MB_OK);
330       exit (EXIT_FAILURE);
331     }
332
333   ShowWindow (wnd, _nCmdShow);
334
335   hp = tp = kbd;
336 }
337
338 LRESULT CALLBACK _export
339 _blp_console_wndproc (HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
340 {
341   char s[80];
342
343   switch (msg)
344     {
345     case WM_CREATE:
346       {
347         HDC dc = GetDC (hwnd);
348         TEXTMETRIC tm;
349         int success;
350
351         SelectObject (dc, GetStockObject (ANSI_FIXED_FONT));
352         success = GetTextMetrics (dc, &tm);
353         assert (success);
354         cw = tm.tmMaxCharWidth;
355         ch = tm.tmHeight;
356         success = ReleaseDC (hwnd, dc);
357         assert (success);
358         return 0;
359       }
360     case WM_PAINT:
361       _blp_console_paint ();
362       return 0;
363     case WM_CHAR:
364       {
365         if (hp + 1 != tp && (hp != &kbd[MAX_KBD_BUF - 1] || tp != kbd))
366           {
367             *hp++ = wp;
368             if (hp >= &kbd[MAX_KBD_BUF])
369               hp = kbd;
370           }
371       }
372       break;
373     }
374   return DefWindowProc (hwnd, msg, wp, lp);
375 }
376
377 static void
378 find_console_top (line ** top)
379 {
380   int success;
381
382   /* Line count. */
383   int lc;
384
385   /* Line iterator. */
386   line *iter;
387
388   /* Scratch line. */
389   static line temp;
390
391   /* Client rectangle. */
392   RECT r;
393
394   success = GetClientRect (wnd, &r);
395   assert (success);
396   nw = r.right / cw;
397   if (nw < 1)
398     nw = 1;
399   nh = r.bottom / ch;
400   if (nh < 1)
401     nh = 1;
402
403   /* Find the beginning of the text to display. */
404   for (lc = 0, iter = tail; iter; iter = iter->prev)
405     {
406       if (!iter->len)
407         lc++;
408       else
409         lc += (iter->len / nw) + (iter->len % nw > 0);
410       if (lc >= nh || !iter->prev)
411         break;
412     }
413   if (lc > nh)
414     {
415       temp = *iter;
416       temp.text += nw * (lc - nh);
417       temp.len -= nw * (lc - nh);
418       *top = &temp;
419     }
420   else
421     *top = iter;
422 }
423
424 static void
425 find_console_bottom (int *x, int *y, line ** bottom)
426 {
427   find_console_top (bottom);
428   *x = *y = 0;
429   if (!*bottom)
430     return;
431   while (1)
432     {
433       if ((*bottom)->len == 0)
434         (*y)++;
435       else
436         (*y) += ((*bottom)->len / nw) + ((*bottom)->len % nw > 0);
437       if (!(*bottom)->next)
438         break;
439       *bottom = (*bottom)->next;
440     }
441   *x = (*bottom)->len % nw;
442   (*y)--;
443 }
444
445 void
446 _blp_console_paint (void)
447 {
448   PAINTSTRUCT ps;
449   HDC dc;
450
451   /* Current screen location. */
452   int x, y;
453
454   /* Current line. */
455   line *iter;
456
457   dc = BeginPaint (wnd, &ps);
458   assert (dc);
459
460   find_console_top (&iter);
461
462   /* Display the text. */
463   SelectObject (dc, GetStockObject (ANSI_FIXED_FONT));
464   SetTextColor (dc, RGB (0, 0, 0));
465   for (y = 0; iter; iter = iter->next)
466     {
467       if (!iter->len)
468         {
469           y += ch;
470           continue;
471         }
472       for (x = 0; x < iter->len; x += nw)
473         {
474           TextOut (dc, 0, y, &iter->text[x],
475                    iter->len - x > nw ? nw : iter->len - x);
476           y += ch;
477         }
478     }
479
480   EndPaint (wnd, &ps);
481 }
482
483 int main (int argc, char *argv[], char *env[]);
484
485 #if __BORLANDC__
486 #pragma argsused
487 #endif
488 int CALLBACK
489 WinMain (HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int nCmdShow)
490 {
491   int result;
492   MSG msg;
493
494   char *pgmname = "PSPP";
495
496   _hInstance = inst;
497   _hPrev = prev;
498   _cmdline = cmdline;
499   _nCmdShow = nCmdShow;
500
501   result = main (1, &pgmname, NULL);
502
503   return result;
504 }