3816 lines
		
	
	
		
			84 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			3816 lines
		
	
	
		
			84 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*  Part of SWI-Prolog
 | |
| 
 | |
|     Author:        Jan Wielemaker
 | |
|     E-mail:        J.Wielemaker@vu.nl
 | |
|     WWW:           http://www.swi-prolog.org
 | |
|     Copyright (C): 1985-2012, University of Amsterdam
 | |
| 			      VU University Amsterdam
 | |
| 
 | |
|     This library is free software; you can redistribute it and/or
 | |
|     modify it under the terms of the GNU Lesser General Public
 | |
|     License as published by the Free Software Foundation; either
 | |
|     version 2.1 of the License, or (at your option) any later version.
 | |
| 
 | |
|     This library is distributed in the hope that it will be useful,
 | |
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|     Lesser General Public License for more details.
 | |
| 
 | |
|     You should have received a copy of the GNU Lesser General Public
 | |
|     License along with this library; if not, write to the Free Software
 | |
|     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 | |
| */
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| This file defines a console for porting (unix) stream-based applications
 | |
| to MS-Windows. It has been developed for  SWI-Prolog. The main source is
 | |
| part of SWI-Prolog.
 | |
| 
 | |
| The SWI-Prolog source is at http://www.swi-prolog.org
 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| Thread design:
 | |
| 
 | |
| <written as a mail to Lutz Wohlrab>
 | |
| 
 | |
| There are two threads. The Prolog engine   runs  in the main thread. The
 | |
| other thread deals with the window.   Basically, it processes events and
 | |
| if anything is typed it puts it into a queue.
 | |
| 
 | |
| The main thread  at  some  stage   forks  the  display  thread,  running
 | |
| window_loop().  This  thread  initialises  the   input  and  then  sends
 | |
| WM_RLC_READY to the main thread to indicate it is ready to accept data.
 | |
| 
 | |
| If data is to be written,  Prolog   calls  rlc_write(),  which posts the
 | |
| WM_RLC_WRITE to the display thread, waiting  on the termination. If data
 | |
| is to be read, rlc_read() posts  a   WM_RLC_FLUSH,  and then waits while
 | |
| dispatching events, for the display-thread to   fill the buffer and send
 | |
| WM_RLC_INPUT (which is just sent  to   make  GetMessage()  in rlc_read()
 | |
| return).
 | |
| 
 | |
| Towards an MT version on Windows
 | |
| --------------------------------
 | |
| 
 | |
| If we want to move towards a  multi-threaded version for MS-Windows, the
 | |
| console code needs to be changed significantly, as we need to be able to
 | |
| create multiple consoles to support thread_attach_console/0.
 | |
| 
 | |
| The most logical solution seems to   be to reverse the thread-structure,
 | |
| Prolog starting and running in the   main-thread  and creating a console
 | |
| creates a new thread for this console. There  are two ways to keep track
 | |
| of the console to use. Cleanest might be to add an argument denoting the
 | |
| allocated console and alternatively we could   use thread-local data. We
 | |
| can also combine the two: add an  additional argument, but allow passing
 | |
| NULL to use the default console for this thread.
 | |
| 
 | |
| Menus
 | |
| -----
 | |
| 
 | |
| The current console provides a menu that can be extended from Prolog.
 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| #ifdef O_DEBUG_HEAP
 | |
| static void initHeapDebug(void);
 | |
| #include <crtdbg.h>
 | |
| #else
 | |
| #define initHeapDebug()
 | |
| #endif
 | |
| 
 | |
| #include <windows.h>
 | |
| #include <tchar.h>
 | |
| #ifndef WM_MOUSEWHEEL			/* sometimes not defined */
 | |
| #define WM_MOUSEWHEEL 0x020A
 | |
| #endif
 | |
| #ifndef WM_UNICHAR
 | |
| #define WM_UNICHAR 0x109
 | |
| #define UNICODE_NOCHAR 0xFFFF
 | |
| #endif
 | |
| 
 | |
| #if (_MSC_VER < 1400) && !defined(__MINGW32__)
 | |
| typedef DWORD DWORD_PTR;
 | |
| #endif
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <io.h>
 | |
| #include <string.h>
 | |
| #include <malloc.h>
 | |
| #define _MAKE_DLL 1
 | |
| #undef _export
 | |
| #include "console.h"
 | |
| #include "menu.h"
 | |
| #include "common.h"
 | |
| #include <signal.h>
 | |
| #include <ctype.h>
 | |
| #include <stdio.h>
 | |
| 
 | |
| #ifndef isletter
 | |
| #define isletter(c) (_istalpha(c) || (c) == '_')
 | |
| #endif
 | |
| 
 | |
| #ifndef MAXPATHLEN
 | |
| #define MAXPATHLEN 256
 | |
| #endif
 | |
| 
 | |
| #ifndef CHAR_MAX
 | |
| #define CHAR_MAX 256
 | |
| #endif
 | |
| 
 | |
| #define MAXLINE	     1024		/* max chars per line */
 | |
| 
 | |
| #define CMD_INITIAL	0
 | |
| #define CMD_ESC		1
 | |
| #define CMD_ANSI	2
 | |
| 
 | |
| #define GWL_DATA	0		/* offset for client data */
 | |
| 
 | |
| #define CHG_RESET	0		/* unchenged */
 | |
| #define CHG_CHANGED	1		/* changed, but no clear */
 | |
| #define CHG_CLEAR	2		/* clear */
 | |
| #define CHG_CARET	4		/* caret has moved */
 | |
| 
 | |
| #define SEL_CHAR	0		/* character-unit selection */
 | |
| #define SEL_WORD	1		/* word-unit selection */
 | |
| #define SEL_LINE	2		/* line-unit selection */
 | |
| 
 | |
| #ifndef EOS
 | |
| #define EOS 0
 | |
| #endif
 | |
| 
 | |
| #define ESC 27				/* the escape character */
 | |
| 
 | |
| #define WM_RLC_INPUT	 WM_USER+10	/* Just somewhere ... */
 | |
| #define WM_RLC_WRITE	 WM_USER+11	/* write data */
 | |
| #define WM_RLC_FLUSH	 WM_USER+12	/* flush buffered data */
 | |
| #define WM_RLC_READY	 WM_USER+13	/* Window thread is ready */
 | |
| #define WM_RLC_CLOSEWIN  WM_USER+14	/* Close the window */
 | |
| /*#define WM_RLC_MENU	 WM_USER+15	   Insert a menu (defined in menu.h) */
 | |
| 
 | |
| #define IMODE_RAW	1		/* char-by-char */
 | |
| #define IMODE_COOKED	2		/* line-by-line */
 | |
| 
 | |
| #define NextLine(b, i) ((i) < (b)->height-1 ? (i)+1 : 0)
 | |
| #define PrevLine(b, i) ((i) > 0 ? (i)-1 : (b)->height-1)
 | |
| #define Bounds(v, mn, mx) ((v) < (mn) ? (mn) : (v) > (mx) ? (mx) : (v))
 | |
| 
 | |
| #define Control(x) ((x) - '@')
 | |
| 
 | |
| #define streq(s, q) (_tcscmp((s), (q)) == 0)
 | |
| 
 | |
| #include "console_i.h"			/* internal package stuff */
 | |
| 
 | |
| #define OPT_SIZE	0x01
 | |
| #define OPT_POSITION	0x02
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	       DATA		*
 | |
| 		 *******************************/
 | |
| 
 | |
|        RlcData  _rlc_stdio = NULL;	/* the main buffer */
 | |
| static int      _rlc_show;		/* initial show */
 | |
| static char	_rlc_word_chars[CHAR_MAX]; /* word-characters (selection) */
 | |
| static const TCHAR *	_rlc_program;		/* name of the program */
 | |
| static HANDLE   _rlc_hinstance;		/* Global instance */
 | |
| static HICON    _rlc_hicon;		/* Global icon */
 | |
| 
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	     FUNCTIONS		*
 | |
| 		 *******************************/
 | |
| 
 | |
| static LRESULT WINAPI rlc_wnd_proc(HWND win, UINT msg, WPARAM wP, LPARAM lP);
 | |
| 
 | |
| static void	rcl_setup_ansi_colors(RlcData b);
 | |
| static void	rlc_place_caret(RlcData b);
 | |
| static void	rlc_resize_pixel_units(RlcData b, int w, int h);
 | |
| static RlcData	rlc_make_buffer(int w, int h);
 | |
| static int	rlc_count_lines(RlcData b, int from, int to);
 | |
| static void	rlc_add_line(RlcData b);
 | |
| static void	rlc_open_line(RlcData b);
 | |
| static void	rlc_update_scrollbar(RlcData b);
 | |
| static void	rlc_paste(RlcData b);
 | |
| static void	rlc_init_text_dimensions(RlcData b, HFONT f);
 | |
| static void	rlc_save_font_options(HFONT f, rlc_console_attr *attr);
 | |
| static void	rlc_get_options(rlc_console_attr *attr);
 | |
| static HKEY	rlc_option_key(rlc_console_attr *attr, int create);
 | |
| static void	rlc_progbase(TCHAR *path, TCHAR *base);
 | |
| static int	rlc_add_queue(RlcData b, RlcQueue q, int chr);
 | |
| static int	rlc_add_lines(RlcData b, int here, int add);
 | |
| static void	rlc_start_selection(RlcData b, int x, int y);
 | |
| static void	rlc_extend_selection(RlcData b, int x, int y);
 | |
| static void	rlc_word_selection(RlcData b, int x, int y);
 | |
| static int	rlc_has_selection(RlcData b);
 | |
| static void	rlc_set_selection(RlcData b, int sl, int sc, int el, int ec);
 | |
| static void	rlc_copy(RlcData b);
 | |
| static void	rlc_destroy(RlcData b);
 | |
| static void	rlc_request_redraw(RlcData b);
 | |
| static void	rlc_redraw(RlcData b);
 | |
| static int	rlc_breakargs(TCHAR *line, TCHAR **argv);
 | |
| static void	rlc_resize(RlcData b, int w, int h);
 | |
| static void	rlc_adjust_line(RlcData b, int line);
 | |
| static int	text_width(RlcData b, HDC hdc, const text_char *text, int len);
 | |
| static int	tchar_width(RlcData b, HDC hdc, const TCHAR *text, int len);
 | |
| static void	rlc_queryfont(RlcData b);
 | |
| static void     rlc_do_write(RlcData b, TCHAR *buf, int count);
 | |
| static void     rlc_reinit_line(RlcData b, int line);
 | |
| static void	rlc_free_line(RlcData b, int line);
 | |
| static int	rlc_between(RlcData b, int f, int t, int v);
 | |
| static void	free_user_data(RlcData b);
 | |
| 
 | |
| static RlcQueue	rlc_make_queue(int size);
 | |
| static int	rlc_from_queue(RlcQueue q);
 | |
| static int	rlc_is_empty_queue(RlcQueue q);
 | |
| 
 | |
| extern int	main(int argc, char *argv[]);
 | |
| 
 | |
| static RlcUpdateHook	_rlc_update_hook;
 | |
| static RlcTimerHook	_rlc_timer_hook;
 | |
| static RlcRenderHook	_rlc_render_hook;
 | |
| static RlcRenderAllHook _rlc_render_all_hook;
 | |
| static RlcInterruptHook _rlc_interrupt_hook;
 | |
| static RlcResizeHook    _rlc_resize_hook;
 | |
| static RlcMenuHook	_rlc_menu_hook;
 | |
| static RlcMessageHook	_rlc_message_hook;
 | |
| static int _rlc_copy_output_to_debug_output=0;	/* != 0: copy to debugger */
 | |
| static int	emulate_three_buttons;
 | |
| static HWND	emu_hwnd;		/* Emulating for this window */
 | |
| 
 | |
| static void _rlc_create_kill_window(RlcData b);
 | |
| static DWORD WINAPI window_loop(LPVOID arg);	/* console window proc */
 | |
| 
 | |
| #ifdef _DEBUG
 | |
| #include <stdarg.h>
 | |
| static void Dprintf(const TCHAR *fmt, ...);
 | |
| static void Dprint_lines(RlcData b, int from, int to);
 | |
| #define DEBUG(Code) Code
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| It might look a bit weird not to  use <assert.h>, but for some reason it
 | |
| looks as if the application thread continues if the asserting is trapped
 | |
| using  the  normal  assert()!?  Just  but    a  debugger  breakpoint  on
 | |
| rlc_assert() and all functions normally.
 | |
| 
 | |
| rlc_check_assertions() is a (very) incomplete   check that everything we
 | |
| expect to be true about the data is indeed the case.
 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| void
 | |
| rlc_assert(const TCHAR *msg)
 | |
| { MessageBox(NULL, msg, _T("Console assertion failed"), MB_OK|MB_TASKMODAL);
 | |
| }
 | |
| 
 | |
| void
 | |
| rlc_check_assertions(RlcData b)
 | |
| { int window_last = rlc_add_lines(b, b->window_start, b->window_size-1);
 | |
|   int y;
 | |
| 
 | |
|   assert(b->last != b->first || b->first == 0);
 | |
|   assert(b->caret_x >= 0 && b->caret_x < b->width);
 | |
| 					/* TBD: debug properly */
 | |
| /*assert(rlc_between(b, b->window_start, window_last, b->caret_y));*/
 | |
| 
 | |
|   for(y=0; y<b->height; y++)
 | |
|   { TextLine tl = &b->lines[y];
 | |
| 
 | |
|     assert(tl->size >= 0 && tl->size <= b->width);
 | |
|   }
 | |
| }
 | |
| 
 | |
| #else
 | |
| 
 | |
| #define DEBUG(Code) ((void)0)
 | |
| #define rlc_check_assertions(b)
 | |
| #endif
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| rlc_long_name(TCHAR *buffer)
 | |
| 	Translate a filename, possibly holding 8+3 abbreviated parts into
 | |
| 	the `real' filename.  I couldn't find a direct call for this.  If
 | |
| 	you have it, I'd be glad to receive a better implementation.
 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| static void
 | |
| rlc_long_name(TCHAR *file)
 | |
| { TCHAR buf[MAXPATHLEN];
 | |
|   TCHAR *i = file;
 | |
|   TCHAR *o = buf;
 | |
|   TCHAR *ok = buf;
 | |
|   int changed = 0;
 | |
| 
 | |
|   while(*i)
 | |
|   { int dirty = FALSE;
 | |
| 
 | |
|     while(*i && *i != '\\')
 | |
|     { if ( *i == '~' )
 | |
| 	dirty++;
 | |
|       *o++ = *i++;
 | |
|     }
 | |
|     if ( dirty )
 | |
|     { WIN32_FIND_DATA data;
 | |
|       HANDLE h;
 | |
| 
 | |
|       *o = '\0';
 | |
|       if ( (h=FindFirstFile(buf, &data)) != INVALID_HANDLE_VALUE )
 | |
|       { _tcscpy(ok, data.cFileName);
 | |
| 	FindClose(h);
 | |
| 	o = ok + _tcslen(ok);
 | |
| 	changed++;
 | |
|       }
 | |
|     }
 | |
|     if ( *i )
 | |
|       *o++ = *i++;
 | |
|     ok = o;
 | |
|   }
 | |
| 
 | |
|   if ( changed )
 | |
|   { *o = '\0';
 | |
|     _tcscpy(file, buf);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| If %PLTERM_CLASS% is in the environment, this   value is used as Windows
 | |
| class identifier for the console window.   This allows external programs
 | |
| to start PLWIN.EXE and find the window it  has started in order to embed
 | |
| it.
 | |
| 
 | |
| In old versions this was fixed to "RlcConsole"
 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| static TCHAR *
 | |
| rlc_window_class(HICON icon)
 | |
| { static TCHAR winclassname[32];
 | |
|   static WNDCLASS wndClass;
 | |
|   HINSTANCE instance = _rlc_hinstance;
 | |
| 
 | |
|   if ( !winclassname[0] )
 | |
|   { if ( !GetEnvironmentVariable(_T("PLTERM_CLASS"),
 | |
| 				 winclassname, sizeof(winclassname)) )
 | |
|       snwprintf(winclassname, sizeof(winclassname)/sizeof(TCHAR),
 | |
| 		_T("PlTerm-%d"), instance);
 | |
| 
 | |
|     wndClass.lpszClassName	= winclassname;
 | |
|     wndClass.style		= CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
 | |
|     wndClass.lpfnWndProc	= (LPVOID) rlc_wnd_proc;
 | |
|     wndClass.cbClsExtra		= 0;
 | |
|     wndClass.cbWndExtra		= sizeof(intptr_t);
 | |
|     wndClass.hInstance		= instance;
 | |
|     if ( icon )
 | |
|       wndClass.hIcon		= icon;
 | |
|     else
 | |
|       wndClass.hIcon		= LoadIcon(NULL, IDI_APPLICATION);
 | |
|     wndClass.hCursor		= LoadCursor(NULL, IDC_IBEAM);
 | |
|     wndClass.hbrBackground	= (HBRUSH) NULL;
 | |
|     wndClass.lpszMenuName	= NULL;
 | |
| 
 | |
|     RegisterClass(&wndClass);
 | |
|   }
 | |
| 
 | |
|   return winclassname;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| There are two ways to get the commandline.   It  is passed to WinMain as
 | |
| 8-bit string. This version does *not* include  the command itself. It is
 | |
| also available through GetCommandLine(), which  does include the command
 | |
| itself and returns LPTSTR (Unicode/ANSI). We assume the latter.
 | |
| 
 | |
| Nevertheless, for backward compatibility as well  as easy to extract the
 | |
| full pathname of the  executable,  we   replace  argv[0]  with  the intptr_t
 | |
| filename version of the current module, so argv[0] is guaranteed to be a
 | |
| full path refering to the .exe file.
 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| int
 | |
| rlc_main(HANDLE hInstance, HANDLE hPrevInstance,
 | |
| 	 LPTSTR lpszCmdLine, int nCmdShow,
 | |
| 	 RlcMain mainfunc, HICON icon)
 | |
| { TCHAR *	    argv[100];
 | |
|   int		    argc;
 | |
|   TCHAR		    program[MAXPATHLEN];
 | |
|   TCHAR		    progbase[100];
 | |
|   RlcData           b;
 | |
|   rlc_console_attr  attr;
 | |
| 
 | |
|   initHeapDebug();
 | |
| 
 | |
|   _rlc_hinstance = hInstance;
 | |
|   _rlc_show = nCmdShow;
 | |
|   _rlc_hicon = icon;
 | |
| 
 | |
|   GetModuleFileName(hInstance, program, sizeof(program));
 | |
|   rlc_long_name(program);
 | |
|   argc = rlc_breakargs(lpszCmdLine, argv);
 | |
|   argv[0] = program;
 | |
|   rlc_progbase(argv[0], progbase);
 | |
| 
 | |
|   memset(&attr, 0, sizeof(attr));
 | |
|   _rlc_program = attr.title = progbase;
 | |
|   _rlc_stdio = b = rlc_create_console(&attr);
 | |
| 
 | |
|   if ( mainfunc )
 | |
|     return (*mainfunc)(b, argc, argv);
 | |
|   else
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| rlc_console
 | |
| rlc_create_console(rlc_console_attr *attr)
 | |
| { RlcData b;
 | |
|   MSG msg;
 | |
|   const TCHAR *title;
 | |
| 
 | |
|   rlc_get_options(attr);
 | |
| 
 | |
|   if ( attr->title )
 | |
|     title = attr->title;
 | |
|   else
 | |
|     title = _T("Untitled");
 | |
| 
 | |
|   b = rlc_make_buffer(attr->width, attr->savelines);
 | |
|   b->create_attributes = attr;
 | |
|   _tcscpy(b->current_title, title);
 | |
|   if ( attr->key )
 | |
|   { b->regkey_name = _tcsdup(attr->key);
 | |
|   }
 | |
| 
 | |
|   rlc_init_text_dimensions(b, NULL);
 | |
|   _rlc_create_kill_window(b);
 | |
| 
 | |
|   DuplicateHandle(GetCurrentProcess(),
 | |
| 		  GetCurrentThread(),
 | |
| 		  GetCurrentProcess(),
 | |
| 		  &b->application_thread,
 | |
| 		  0,
 | |
| 		  FALSE,
 | |
| 		  DUPLICATE_SAME_ACCESS);
 | |
|   b->application_thread_id = GetCurrentThreadId();
 | |
|   b->console_thread = CreateThread(NULL,			/* security */
 | |
| 				   2048,			/* stack */
 | |
| 				   window_loop, b,		/* proc+arg */
 | |
| 				   0,				/* flags */
 | |
| 				   &b->console_thread_id);	/* id */
 | |
| 					/* wait till the window is created */
 | |
|   GetMessage(&msg, NULL, WM_RLC_READY, WM_RLC_READY);
 | |
|   b->create_attributes = NULL;		/* release this data */
 | |
| 
 | |
|   return b;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_create_window(RlcData b)
 | |
| { HWND hwnd;
 | |
|   rlc_console_attr *a = b->create_attributes;
 | |
|   RECT rect;
 | |
|   DWORD style = (WS_OVERLAPPEDWINDOW|WS_VSCROLL);
 | |
| 
 | |
| /* One would assume AdjustWindowRect() uses WS_VSCROLL to add the width of
 | |
|    the scrollbar.  I think this isn't true, but maybe there is another reason
 | |
|    for getting 2 characters shorter each invocation ...
 | |
| */
 | |
| 
 | |
|   rect.left   = a->x;
 | |
|   rect.top    = a->y;
 | |
|   rect.right  = a->x + (a->width+2) * b->cw + GetSystemMetrics(SM_CXVSCROLL);
 | |
|   rect.bottom = a->y + a->height * b->ch;
 | |
| 
 | |
|   AdjustWindowRect(&rect, style, TRUE);
 | |
|   hwnd = CreateWindow(rlc_window_class(_rlc_hicon), b->current_title,
 | |
| 		      style,
 | |
| 		      a->x, a->y,
 | |
| 		      rect.right - rect.left,
 | |
| 		      rect.bottom - rect.top,
 | |
| 		      NULL, NULL, _rlc_hinstance, NULL);
 | |
| 
 | |
|   b->window = hwnd;
 | |
|   SetWindowLongPtr(hwnd, GWL_DATA, (LONG_PTR) b);
 | |
|   SetScrollRange(hwnd, SB_VERT, 0, b->sb_lines, FALSE);
 | |
|   SetScrollPos(hwnd, SB_VERT, b->sb_start, TRUE);
 | |
| 
 | |
|   b->queue    = rlc_make_queue(256);
 | |
|   b->sb_lines = rlc_count_lines(b, b->first, b->last);
 | |
|   b->sb_start = rlc_count_lines(b, b->first, b->window_start);
 | |
| 
 | |
|   rcl_setup_ansi_colors(b);
 | |
|   b->foreground = GetSysColor(COLOR_WINDOWTEXT);
 | |
|   b->background = GetSysColor(COLOR_WINDOW);
 | |
|   b->sel_foreground = GetSysColor(COLOR_HIGHLIGHTTEXT);
 | |
|   b->sel_background = GetSysColor(COLOR_HIGHLIGHT);
 | |
|   if ( GetSystemMetrics(SM_CMOUSEBUTTONS) == 2 )
 | |
|     emulate_three_buttons = 120;
 | |
| 
 | |
|   rlc_add_menu_bar(b->window);
 | |
| 
 | |
|   ShowWindow(hwnd, _rlc_show);
 | |
|   UpdateWindow(hwnd);
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| rlc_iswin32s()
 | |
| { if( GetVersion() & 0x80000000 && (GetVersion() & 0xFF) ==3)
 | |
|     return TRUE;
 | |
|   else
 | |
|     return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_progbase(TCHAR *path, TCHAR *base)
 | |
| { TCHAR *s;
 | |
|   TCHAR *e;
 | |
| 
 | |
|   if ( !(s=_tcsrchr(path, '\\')) )
 | |
|     s = path;				/* takes the filename part */
 | |
|   else
 | |
|     s++;
 | |
|   if ( !(e = _tcschr(s, '.')) )
 | |
|     _tcscpy(base, s);
 | |
|   else
 | |
|   { _tcsncpy(base, s, e-s);
 | |
|     base[e-s] = '\0';
 | |
|   }
 | |
| }
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	  HIDDEN WINDOW		*
 | |
| 		 *******************************/
 | |
| 
 | |
| static LRESULT WINAPI
 | |
| rlc_kill_wnd_proc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 | |
| { switch(message)
 | |
|   { case WM_DESTROY:
 | |
|       PostQuitMessage(0);
 | |
|       return 0;
 | |
|   }
 | |
| 
 | |
|   return DefWindowProc(hwnd, message, wParam, lParam);
 | |
| }
 | |
| 
 | |
| static TCHAR *
 | |
| rlc_kill_window_class(void)
 | |
| { static TCHAR winclassname[32];
 | |
|   static WNDCLASS wndClass;
 | |
|   HINSTANCE instance = _rlc_hinstance;
 | |
| 
 | |
|   if ( !winclassname[0] )
 | |
|   { snwprintf(winclassname, sizeof(winclassname)/sizeof(TCHAR),
 | |
| 	      _T("Console-hidden-win%d"), instance);
 | |
| 
 | |
|     wndClass.style		= 0;
 | |
|     wndClass.lpfnWndProc	= (LPVOID) rlc_kill_wnd_proc;
 | |
|     wndClass.cbClsExtra		= 0;
 | |
|     wndClass.cbWndExtra		= 0;
 | |
|     wndClass.hInstance		= instance;
 | |
|     wndClass.hIcon		= NULL;
 | |
|     wndClass.hCursor		= NULL;
 | |
|     wndClass.hbrBackground	= GetStockObject(WHITE_BRUSH);
 | |
|     wndClass.lpszMenuName	= NULL;
 | |
|     wndClass.lpszClassName	= winclassname;
 | |
| 
 | |
|     RegisterClass(&wndClass);
 | |
|   }
 | |
| 
 | |
|   return winclassname;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| _rlc_create_kill_window(RlcData b)
 | |
| { b->kill_window = CreateWindow(rlc_kill_window_class(),
 | |
| 				_T("Console hidden window"),
 | |
| 				0,
 | |
| 				0, 0, 32, 32,
 | |
| 				NULL, NULL, _rlc_hinstance, NULL);
 | |
| }
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *     REGISTRY COMMUNICATION	*
 | |
| 		 *******************************/
 | |
| 
 | |
| #define MAXREGSTRLEN 1024
 | |
| 
 | |
| static void
 | |
| reg_save_int(HKEY key, const TCHAR *name, int value)
 | |
| { DWORD val = value;
 | |
| 
 | |
|   if ( RegSetValueEx(key, name, 0,
 | |
| 		     REG_DWORD_LITTLE_ENDIAN,
 | |
| 		     (LPBYTE)&val, sizeof(val)) != ERROR_SUCCESS )
 | |
|     DEBUG(MessageBox(NULL, _T("Failed to save int setting"),
 | |
| 		     _T("Error"), MB_OK));
 | |
| }
 | |
| 
 | |
| static void
 | |
| reg_save_str(HKEY key, const TCHAR *name, TCHAR *value)
 | |
| { if ( RegSetValueEx(key, name, 0, REG_SZ,
 | |
| 		     (LPBYTE)value, (DWORD)(_tcslen(value)+1)*sizeof(TCHAR)) != ERROR_SUCCESS )
 | |
|     DEBUG(MessageBox(NULL, _T("Failed to save string setting"), _T("Error"), MB_OK));
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_save_options(RlcData b)
 | |
| { HKEY key;
 | |
|   rlc_console_attr attr;
 | |
| 
 | |
|   memset(&attr, 0, sizeof(attr));
 | |
|   attr.key = b->regkey_name;
 | |
| 
 | |
|   if ( !(key = rlc_option_key(&attr, TRUE)) )
 | |
|     return;
 | |
| 
 | |
|   reg_save_int(key, _T("SaveLines"),  b->height);
 | |
|   if ( b->modified_options & OPT_SIZE )
 | |
|   { reg_save_int(key, _T("Width"),    b->width);
 | |
|     reg_save_int(key, _T("Height"),   b->window_size);
 | |
|   }
 | |
|   if ( b->modified_options & OPT_POSITION )
 | |
|   { reg_save_int(key, _T("X"),	  b->win_x);
 | |
|     reg_save_int(key, _T("Y"),	  b->win_y);
 | |
|   }
 | |
| 
 | |
|   rlc_save_font_options(b->hfont, &attr);
 | |
|   if ( attr.face_name[0] )
 | |
|   { reg_save_str(key, _T("FaceName"),    attr.face_name);
 | |
|     reg_save_int(key, _T("FontFamily"),  attr.font_family);
 | |
|     reg_save_int(key, _T("FontSize"),    attr.font_size);
 | |
|     reg_save_int(key, _T("FontWeight"),  attr.font_weight);
 | |
|     reg_save_int(key, _T("FontCharSet"), attr.font_char_set);
 | |
|   }
 | |
| 
 | |
|   RegCloseKey(key);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| reg_get_int(HKEY key, const TCHAR *name, int mn, int def, int mx, int *value)
 | |
| { DWORD type;
 | |
|   BYTE  data[8];
 | |
|   DWORD len = sizeof(data);
 | |
| 
 | |
|   if ( *value )
 | |
|     return;				/* use default */
 | |
| 
 | |
|   if ( RegQueryValueEx(key, name, NULL, &type, data, &len) == ERROR_SUCCESS )
 | |
|   { switch(type)
 | |
|     { /*case REG_DWORD:*/		/* Same case !? */
 | |
|       case REG_DWORD_LITTLE_ENDIAN:
 | |
|       { DWORD *valp = (DWORD *)data;
 | |
| 	int v = *valp;
 | |
| 
 | |
| 	if ( mn < mx )
 | |
| 	{ if ( v < mn )
 | |
| 	    v = mn;
 | |
| 	  else if ( v > mx )
 | |
| 	    v = mx;
 | |
| 	}
 | |
| 
 | |
| 	*value = v;
 | |
|       }
 | |
|     }
 | |
|   } else
 | |
|     *value = def;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| reg_get_str(HKEY key, const TCHAR *name, TCHAR *value, int length)
 | |
| { DWORD type;
 | |
|   BYTE  data[MAXREGSTRLEN*sizeof(TCHAR)];
 | |
|   DWORD len = sizeof(data);
 | |
| 
 | |
|   if ( *value )
 | |
|     return;				/* use default */
 | |
| 
 | |
|   if ( RegQueryValueEx(key, name, NULL, &type, data, &len) == ERROR_SUCCESS )
 | |
|   { switch(type)
 | |
|     { case REG_SZ:
 | |
|       { TCHAR *val = (TCHAR*)data;
 | |
| 	_tcsncpy(value, val, length-1);
 | |
| 	value[length-1] = '\0';
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static HKEY
 | |
| reg_open_key(TCHAR **which, int create)
 | |
| { HKEY key = HKEY_CURRENT_USER;
 | |
|   DWORD disp;
 | |
|   LONG rval;
 | |
| 
 | |
|   for( ; *which; which++)
 | |
|   { HKEY tmp;
 | |
| 
 | |
|     if ( which[1] )
 | |
|     { if ( RegOpenKeyEx(key, which[0], 0L, KEY_READ, &tmp) == ERROR_SUCCESS )
 | |
|       { key = tmp;
 | |
| 	continue;
 | |
|       }
 | |
| 
 | |
|       if ( !create )
 | |
| 	return NULL;
 | |
|     }
 | |
| 
 | |
|     rval = RegCreateKeyEx(key, which[0], 0, _T(""), 0,
 | |
| 			  KEY_ALL_ACCESS, NULL, &tmp, &disp);
 | |
|     RegCloseKey(key);
 | |
|     if ( rval == ERROR_SUCCESS )
 | |
|       key = tmp;
 | |
|     else
 | |
|       return NULL;
 | |
|   }
 | |
| 
 | |
|   return key;
 | |
| }
 | |
| 
 | |
| 
 | |
| static HKEY
 | |
| rlc_option_key(rlc_console_attr *attr, int create)
 | |
| { TCHAR Prog[256];
 | |
|   TCHAR *address[] = { _T("Software"),
 | |
| 		       _T(RLC_VENDOR),
 | |
| 		      Prog,
 | |
| 		      _T("Console"),
 | |
| 		      (TCHAR *)attr->key,	/* possible secondary key */
 | |
| 		      NULL
 | |
| 		    };
 | |
|   const TCHAR *s;
 | |
|   TCHAR *q;
 | |
| 
 | |
|   for(s=_rlc_program, q=Prog; *s; s++, q++) /* capitalise the key */
 | |
|   { *q = (s==_rlc_program ? _totupper(*s) : _totlower(*s));
 | |
|   }
 | |
|   *q = EOS;
 | |
| 
 | |
|   return reg_open_key(address, create);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_get_options(rlc_console_attr *attr)
 | |
| { HKEY key;
 | |
| 
 | |
|   if ( !(key = rlc_option_key(attr, FALSE)) )
 | |
|   { if ( !attr->width  )    attr->width = 80;
 | |
|     if ( !attr->height )    attr->height = 24;
 | |
|     if ( !attr->savelines ) attr->savelines = 200;
 | |
| 
 | |
|     return;
 | |
|   }
 | |
| 
 | |
| { int minx, miny, maxx, maxy;
 | |
|   RECT rect;
 | |
| 
 | |
|   SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
 | |
|   minx = rect.top;
 | |
|   miny = rect.left;
 | |
|   maxx = rect.right  - 40;
 | |
|   maxy = rect.bottom - 40;
 | |
| 
 | |
|   reg_get_int(key, _T("SaveLines"),   200,  200, 100000, &attr->savelines);
 | |
|   reg_get_int(key, _T("Width"),        20,	 80,    300, &attr->width);
 | |
|   reg_get_int(key, _T("Height"),        5,	 24,    100, &attr->height);
 | |
|   reg_get_int(key, _T("X"),		 minx, minx,   maxx, &attr->x);
 | |
|   reg_get_int(key, _T("Y"),	         miny, miny,   maxy, &attr->y);
 | |
| }
 | |
| 
 | |
|   reg_get_str(key, _T("FaceName"), attr->face_name,
 | |
| 	      sizeof(attr->face_name)/sizeof(TCHAR));
 | |
|   reg_get_int(key, _T("FontFamily"),    0,  0,  0, &attr->font_family);
 | |
|   reg_get_int(key, _T("FontSize"),      0,  0,  0, &attr->font_size);
 | |
|   reg_get_int(key, _T("FontWeight"),    0,  0,  0, &attr->font_weight);
 | |
|   reg_get_int(key, _T("FontCharSet"),   0,  0,  0, &attr->font_char_set);
 | |
| 
 | |
|   RegCloseKey(key);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| Windows-'95 appears to quote  names  of   files  because  files may hold
 | |
| spaces. rlc_breakargs() will pass a quoted   strings as one argument. If
 | |
| it can't find the closing quote, it  will   tread  the quote as a normal
 | |
| character.
 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| static int
 | |
| rlc_breakargs(TCHAR *line, TCHAR **argv)
 | |
| { int argc = 0;
 | |
| 
 | |
|   while(*line)
 | |
|   { int q;
 | |
| 
 | |
|     while(*line && _istspace(*line))
 | |
|       line++;
 | |
| 
 | |
|     if ( (q = *line) == '"' || q == '\'' )	/* quoted arguments */
 | |
|     { TCHAR *start = line+1;
 | |
|       TCHAR *end = start;
 | |
| 
 | |
|       while( *end && *end != q )
 | |
| 	end++;
 | |
|       if ( *end == q )
 | |
|       { *end = '\0';
 | |
|         argv[argc++] = start;
 | |
| 	line = end+1;
 | |
| 	continue;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if ( *line )
 | |
|     { argv[argc++] = line;
 | |
|       while(*line && !_istspace(*line))
 | |
| 	line++;
 | |
|       if ( *line )
 | |
| 	*line++ = '\0';
 | |
|     }
 | |
|   }
 | |
|   argv[argc] = NULL;			/* add trailing NULL pointer to argv */
 | |
| 
 | |
|   return argc;
 | |
| }
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	    ANSI COLORS		*
 | |
| 		 *******************************/
 | |
| 
 | |
| /* See http://en.wikipedia.org/wiki/ANSI_escape_code */
 | |
| 
 | |
| static void
 | |
| rcl_setup_ansi_colors(RlcData b)
 | |
| { b->sgr_flags = TF_DEFAULT;
 | |
| 
 | |
| #ifdef ANSI_VGA_COLORS
 | |
| 					/* normal versions */
 | |
|   b->ansi_color[0]  = RGB(  0,  0,  0);	/* black */
 | |
|   b->ansi_color[1]  = RGB(170,  0,  0);	/* red */
 | |
|   b->ansi_color[2]  = RGB(0,  170,  0);	/* green */
 | |
|   b->ansi_color[3]  = RGB(170, 85,  0);	/* yellow */
 | |
|   b->ansi_color[4]  = RGB(  0,  0,170);	/* blue */
 | |
|   b->ansi_color[5]  = RGB(170,  0,170);	/* magenta */
 | |
|   b->ansi_color[6]  = RGB(  0,170,170);	/* cyan */
 | |
|   b->ansi_color[7]  = RGB(170,170,170);	/* white */
 | |
| 					/* bright/light versions */
 | |
|   b->ansi_color[8]  = RGB( 85, 85, 85);	/* black */
 | |
|   b->ansi_color[9]  = RGB(255, 85, 85);	/* red */
 | |
|   b->ansi_color[10] = RGB( 85,255, 85);	/* green */
 | |
|   b->ansi_color[11] = RGB(255,255, 85);	/* yellow */
 | |
|   b->ansi_color[12] = RGB( 85, 85,255);	/* blue */
 | |
|   b->ansi_color[13] = RGB(255, 85,255);	/* magenta */
 | |
|   b->ansi_color[14] = RGB( 85,255,255);	/* cyan */
 | |
|   b->ansi_color[15] = RGB(255,255,255);	/* white */
 | |
| #else /*XTERM*/
 | |
| 					/* normal versions */
 | |
|   b->ansi_color[0]  = RGB(  0,  0,  0);	/* black */
 | |
|   b->ansi_color[1]  = RGB(205,  0,  0);	/* red */
 | |
|   b->ansi_color[2]  = RGB(0,  205,  0);	/* green */
 | |
|   b->ansi_color[3]  = RGB(205,205,  0);	/* yellow */
 | |
|   b->ansi_color[4]  = RGB(  0,  0,238);	/* blue */
 | |
|   b->ansi_color[5]  = RGB(205,  0,205);	/* magenta */
 | |
|   b->ansi_color[6]  = RGB(  0,205,205);	/* cyan */
 | |
|   b->ansi_color[7]  = RGB(229,229,229);	/* white */
 | |
| 					/* bright/light versions */
 | |
|   b->ansi_color[8]  = RGB(127,127,127);	/* black */
 | |
|   b->ansi_color[9]  = RGB(255,  0,  0);	/* red */
 | |
|   b->ansi_color[10] = RGB(  0,255,  0);	/* green */
 | |
|   b->ansi_color[11] = RGB(255,255,  0);	/* yellow */
 | |
|   b->ansi_color[12] = RGB( 92, 92,255);	/* blue */
 | |
|   b->ansi_color[13] = RGB(255,  0,255);	/* magenta */
 | |
|   b->ansi_color[14] = RGB(  0,255,255);	/* cyan */
 | |
|   b->ansi_color[15] = RGB(255,255,255);	/* white */
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	     ATTRIBUTES		*
 | |
| 		 *******************************/
 | |
| 
 | |
| COLORREF
 | |
| rlc_color(rlc_console con, int which, COLORREF c)
 | |
| { HDC hdc;
 | |
|   COLORREF old;
 | |
|   RlcData b = rlc_get_data(con);
 | |
| 
 | |
|   hdc = GetDC(NULL);
 | |
|   c = GetNearestColor(hdc, c);
 | |
|   ReleaseDC(NULL, hdc);
 | |
| 
 | |
|   switch(which)
 | |
|   { case RLC_WINDOW:
 | |
|       old = b->background;
 | |
|       b->background = c;
 | |
|       break;
 | |
|     case RLC_TEXT:
 | |
|       old = b->foreground;
 | |
|       b->foreground = c;
 | |
|       break;
 | |
|     case RLC_HIGHLIGHT:
 | |
|       old = b->sel_background;
 | |
|       b->sel_background = c;
 | |
|       break;
 | |
|     case RLC_HIGHLIGHTTEXT:
 | |
|       old = b->sel_foreground;
 | |
|       b->sel_foreground = c;
 | |
|       break;
 | |
|     default:
 | |
|       return (COLORREF)-1;
 | |
|   }
 | |
| 
 | |
|   if ( b->window )
 | |
|     InvalidateRect(b->window, NULL, TRUE);
 | |
| 
 | |
|   return old;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| rlc_kill(RlcData b)
 | |
| { DWORD_PTR result;
 | |
| 
 | |
|   switch(b->closing++)
 | |
|   { case 0:
 | |
|       b->queue->flags |= RLC_EOF;
 | |
|       PostThreadMessage(b->application_thread_id, WM_RLC_INPUT, 0, 0);
 | |
|       return TRUE;
 | |
|     case 1:
 | |
|       if ( _rlc_interrupt_hook )
 | |
|       { (*_rlc_interrupt_hook)(b, SIGINT);
 | |
| 	return TRUE;
 | |
|       }
 | |
|     default:
 | |
|       if ( !SendMessageTimeout(b->kill_window,
 | |
| 			       WM_DESTROY,
 | |
| 			       0, 0,
 | |
| 			       SMTO_ABORTIFHUNG,
 | |
| 			       5000,
 | |
| 			       &result) )
 | |
|       { if ( b->window )
 | |
| 	{ switch( MessageBox(b->window,
 | |
| 			     _T("Main task is not responding.")
 | |
| 			     _T("Click \"OK\" to terminate it"),
 | |
| 			     _T("Error"),
 | |
| 			     MB_OKCANCEL|MB_ICONEXCLAMATION|MB_APPLMODAL) )
 | |
| 	  { case IDCANCEL:
 | |
| 	      return FALSE;
 | |
| 	  }
 | |
| 	  TerminateThread(b->application_thread, 1);
 | |
| 
 | |
| 	  return TRUE;
 | |
| 	}
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_interrupt(RlcData b)
 | |
| { if ( _rlc_interrupt_hook )
 | |
|     (*_rlc_interrupt_hook)((rlc_console)b, SIGINT);
 | |
|   else
 | |
|     raise(SIGINT);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| typed_char(RlcData b, int chr)
 | |
| { if ( chr == Control('C') && rlc_has_selection(b) )
 | |
|   { rlc_copy(b);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   rlc_set_selection(b, 0, 0, 0, 0);
 | |
| 
 | |
|   if ( chr == Control('C') )
 | |
|     rlc_interrupt(b);
 | |
|   else if ( chr == Control('V') || chr == Control('Y') )
 | |
|     rlc_paste(b);
 | |
|   else if ( b->queue )
 | |
|     rlc_add_queue(b, b->queue, chr);
 | |
| }
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	 WINDOW PROCEDURE	*
 | |
| 		 *******************************/
 | |
| 
 | |
| static void
 | |
| rlc_destroy(RlcData b)
 | |
| { if ( b && b->window )
 | |
|   { DestroyWindow(b->window);
 | |
|     b->window = NULL;
 | |
|     b->closing = 3;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| IsDownKey(int code)
 | |
| { short mask = GetKeyState(code);
 | |
| 
 | |
|   return mask & 0x8000;
 | |
| }
 | |
| 
 | |
| 
 | |
| static LRESULT WINAPI
 | |
| rlc_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 | |
| { RlcData b = (RlcData) GetWindowLongPtr(hwnd, GWL_DATA);
 | |
| 
 | |
|   switch(message)
 | |
|   { case WM_CREATE:
 | |
|       return 0;
 | |
| 
 | |
|     case WM_SIZE:
 | |
|       if ( wParam != SIZE_MINIMIZED )
 | |
|       { rlc_resize_pixel_units(b, LOWORD(lParam), HIWORD(lParam));
 | |
| 	b->modified_options |= OPT_SIZE;
 | |
|       }
 | |
|       return 0;
 | |
| 
 | |
|     case WM_MOVE:
 | |
|     { WINDOWPLACEMENT placement;
 | |
| 
 | |
|       placement.length = sizeof(placement);
 | |
|       GetWindowPlacement(hwnd, &placement);
 | |
| 
 | |
|       if ( placement.showCmd == SW_SHOWNORMAL )
 | |
|       { b->win_x = placement.rcNormalPosition.left;
 | |
| 	b->win_y = placement.rcNormalPosition.top;
 | |
| 
 | |
| 	b->modified_options |= OPT_POSITION;
 | |
|       }
 | |
| 
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     case WM_SETFOCUS:
 | |
|       b->has_focus = TRUE;
 | |
|       CreateCaret(hwnd, NULL, b->fixedfont ? b->cw : 3, b->ch-1);
 | |
|       rlc_place_caret(b);
 | |
|       return 0;
 | |
| 
 | |
|     case WM_KILLFOCUS:
 | |
|       b->has_focus = FALSE;
 | |
|       b->caret_is_shown = FALSE;
 | |
|       HideCaret(hwnd);
 | |
|       DestroyCaret();
 | |
|       return 0;
 | |
| 
 | |
|     case WM_PAINT:
 | |
|       rlc_redraw(b);
 | |
|       return 0;
 | |
| 
 | |
|     case WM_COMMAND:
 | |
|     { UINT  item  = (UINT) LOWORD(wParam);
 | |
|       const TCHAR *name;
 | |
| 
 | |
|       switch( item )
 | |
|       { case IDM_PASTE:
 | |
| 	  rlc_paste(b);
 | |
| 	  return 0;
 | |
| 	case IDM_COPY:
 | |
| 	  rlc_copy(b);
 | |
| 	  return 0;			/* no op: already done */
 | |
| 	case IDM_CUT:
 | |
| 	  break;			/* TBD: cut */
 | |
| 	case IDM_BREAK:
 | |
| 	  rlc_interrupt(b);
 | |
| 	  break;
 | |
| 	case IDM_FONT:
 | |
| 	  rlc_queryfont(b);
 | |
| 	  return 0;
 | |
| 	case IDM_EXIT:
 | |
| 	  if ( rlc_kill(b) )
 | |
| 	    return 0;
 | |
| 	  break;
 | |
|       }
 | |
| 
 | |
|       if ( (name = lookupMenuId(item)) )
 | |
|       { if ( _rlc_menu_hook )
 | |
| 	{ (*_rlc_menu_hook)(b, name);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   { int chr;
 | |
| 
 | |
|     case WM_KEYDOWN:			/* up is sent only once */
 | |
|     { switch((int) wParam)
 | |
|       { case VK_DELETE:	chr = 127;		break;
 | |
| 	case VK_LEFT:	chr = Control('B');	break;
 | |
| 	case VK_RIGHT:	chr = Control('F');	break;
 | |
| 	case VK_UP:	chr = Control('P');	break;
 | |
| 	case VK_DOWN:	chr = Control('N');	break;
 | |
| 	case VK_HOME:	chr = Control('A');	break;
 | |
| 	case VK_END:	chr = Control('E');	break;
 | |
|         case VK_CANCEL: rlc_interrupt(b);       return 0;
 | |
| 
 | |
|         case VK_PRIOR:			/* page up */
 | |
| 	{ int maxdo = rlc_count_lines(b, b->first, b->window_start);
 | |
| 	  int pagdo = b->window_size - 1;
 | |
| 	  b->window_start = rlc_add_lines(b, b->window_start,
 | |
| 					  -min(maxdo, pagdo));
 | |
| 
 | |
| 	scrolledbykey:
 | |
| 	  rlc_update_scrollbar(b);
 | |
| 	  InvalidateRect(hwnd, NULL, FALSE);
 | |
| 
 | |
| 	  return 0;
 | |
| 	}
 | |
| 	case VK_NEXT:			/* page down */
 | |
| 	{ int maxup = rlc_count_lines(b, b->window_start, b->last);
 | |
| 	  int pagup = b->window_size - 1;
 | |
| 	  b->window_start = rlc_add_lines(b, b->window_start,
 | |
| 					  min(maxup, pagup));
 | |
| 	  goto scrolledbykey;
 | |
| 	}
 | |
| 	default:
 | |
| 	  goto break2;
 | |
|       }
 | |
|       if ( chr > 0 )
 | |
|       { if ( IsDownKey(VK_CONTROL) )
 | |
| 	  typed_char(b, ESC);
 | |
| 
 | |
| 	typed_char(b, chr);
 | |
| 
 | |
| 	return 0;
 | |
|       }
 | |
|     break2:
 | |
|       break;
 | |
|     }
 | |
| 	case WM_UNICHAR:
 | |
| 	  chr = (int)wParam;
 | |
| 	  typed_char(b, chr);
 | |
| 	  return 0;
 | |
|     case WM_SYSCHAR:	typed_char(b, ESC); /* Play escape-something */
 | |
|     case WM_CHAR:	chr = (int)wParam;
 | |
| 
 | |
|       typed_char(b, chr);
 | |
| 
 | |
|       return 0;
 | |
|   }
 | |
| 
 | |
| 					/* selection handling */
 | |
|     case WM_MBUTTONDOWN:
 | |
|     middle_down:
 | |
|       return 0;
 | |
| 
 | |
|     case WM_MBUTTONUP:
 | |
|     middle_up:
 | |
|       rlc_paste(b);
 | |
| 
 | |
|       return 0;
 | |
| 
 | |
|     case WM_LBUTTONDOWN:
 | |
|     { POINTS pt;
 | |
| 
 | |
|       if ( emulate_three_buttons )
 | |
|       { MSG msg;
 | |
| 
 | |
| 	Sleep(emulate_three_buttons);
 | |
| 	if ( PeekMessage(&msg, hwnd,
 | |
| 			 WM_RBUTTONDOWN, WM_RBUTTONDOWN, PM_REMOVE) )
 | |
| 	{ emu_hwnd = hwnd;
 | |
| 	  goto middle_down;
 | |
| 	}
 | |
|       }
 | |
| 
 | |
|       pt = MAKEPOINTS(lParam);
 | |
|       rlc_start_selection(b, pt.x, pt.y);
 | |
| 
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     case WM_LBUTTONUP:
 | |
|     case WM_RBUTTONUP:
 | |
|     if ( emu_hwnd == hwnd )
 | |
|     { if ( wParam & (MK_RBUTTON|MK_LBUTTON) )
 | |
| 	goto middle_up;
 | |
|       else
 | |
|       { emu_hwnd = 0;
 | |
| 	return 0;
 | |
|       }
 | |
|     } else
 | |
|     { rlc_copy(b);
 | |
| 
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     case WM_LBUTTONDBLCLK:
 | |
|     { POINTS pt = MAKEPOINTS(lParam);
 | |
| 
 | |
|       rlc_word_selection(b, pt.x, pt.y);
 | |
| 
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     case WM_RBUTTONDOWN:
 | |
|     { POINTS pt;
 | |
| 
 | |
|       if ( emulate_three_buttons )
 | |
|       { MSG msg;
 | |
| 
 | |
| 	Sleep(emulate_three_buttons);
 | |
| 	if ( PeekMessage(&msg, hwnd,
 | |
| 			 WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE) )
 | |
| 	{ emu_hwnd = hwnd;
 | |
| 	  goto middle_down;
 | |
| 	}
 | |
|       }
 | |
| 
 | |
|       pt = MAKEPOINTS(lParam);
 | |
|       rlc_extend_selection(b, pt.x, pt.y);
 | |
| 
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     case WM_MOUSEMOVE:
 | |
|     { POINTS pt = MAKEPOINTS(lParam);
 | |
| 
 | |
|       if ( (wParam & (MK_LBUTTON|MK_RBUTTON)) &&
 | |
| 	   (wParam & (MK_LBUTTON|MK_RBUTTON)) != (MK_LBUTTON|MK_RBUTTON) )
 | |
|       { rlc_extend_selection(b, pt.x, pt.y);
 | |
| 
 | |
| 	return 0;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case WM_MOUSEWHEEL:
 | |
|     { short angle = (short)HIWORD(wParam);
 | |
| 
 | |
|       if ( angle < 0 )
 | |
|       { if ( b->window_start != b->last )
 | |
| 	  b->window_start = NextLine(b, b->window_start);
 | |
|       } else
 | |
|       { if ( b->window_start != b->first )
 | |
| 	  b->window_start = PrevLine(b, b->window_start);
 | |
|       }
 | |
| 
 | |
|       rlc_update_scrollbar(b);
 | |
|       InvalidateRect(hwnd, NULL, FALSE);
 | |
| 
 | |
|       return 0;
 | |
|     }
 | |
| 					/* scrolling */
 | |
|     case WM_VSCROLL:
 | |
|     { switch( LOWORD(wParam) )
 | |
|       { case SB_LINEUP:
 | |
| 	  if ( b->window_start != b->first )
 | |
| 	    b->window_start = PrevLine(b, b->window_start);
 | |
| 	  break;
 | |
| 	case SB_LINEDOWN:
 | |
| 	  if ( b->window_start != b->last )
 | |
| 	    b->window_start = NextLine(b, b->window_start);
 | |
| 	  break;
 | |
| 	case SB_PAGEUP:
 | |
| 	{ int maxdo = rlc_count_lines(b, b->first, b->window_start);
 | |
| 	  int pagdo = b->window_size - 1;
 | |
| 	  b->window_start = rlc_add_lines(b, b->window_start,
 | |
| 					  -min(maxdo, pagdo));
 | |
| 	  break;
 | |
| 	}
 | |
| 	case SB_PAGEDOWN:
 | |
| 	{ int maxup = rlc_count_lines(b, b->window_start, b->last);
 | |
| 	  int pagup = b->window_size - 1;
 | |
| 	  b->window_start = rlc_add_lines(b, b->window_start,
 | |
| 					  min(maxup, pagup));
 | |
| 	  break;
 | |
| 	}
 | |
| 	case SB_THUMBTRACK:
 | |
| 	  b->window_start = rlc_add_lines(b, b->first, HIWORD(wParam));
 | |
| 	  break;
 | |
|       }
 | |
| 
 | |
|       rlc_update_scrollbar(b);
 | |
|       InvalidateRect(hwnd, NULL, FALSE);
 | |
| 
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     case WM_TIMER:
 | |
|       if ( _rlc_timer_hook && wParam >= RLC_APPTIMER_ID )
 | |
|       { (*_rlc_timer_hook)((int) wParam);
 | |
| 
 | |
| 	return 0;
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case WM_RENDERALLFORMATS:
 | |
|       if ( _rlc_render_all_hook )
 | |
|       { (*_rlc_render_all_hook)();
 | |
| 
 | |
|         return 0;
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case WM_RENDERFORMAT:
 | |
|       if ( _rlc_render_hook && (*_rlc_render_hook)(wParam) )
 | |
|         return 0;
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case WM_ERASEBKGND:
 | |
|     { HDC hdc = (HDC) wParam;
 | |
|       RECT rect;
 | |
|       HBRUSH hbrush;
 | |
|       COLORREF rgb = b->background;
 | |
| 
 | |
|       hbrush = CreateSolidBrush(rgb);
 | |
|       GetClipBox(hdc, &rect);
 | |
|       FillRect(hdc, &rect, hbrush);
 | |
|       DeleteObject(hbrush);
 | |
| 
 | |
|       return 1;				/* non-zero: I've erased it */
 | |
|     }
 | |
| 
 | |
|     case WM_SYSCOLORCHANGE:
 | |
|       b->foreground     = GetSysColor(COLOR_WINDOWTEXT);
 | |
|       b->background     = GetSysColor(COLOR_WINDOW);
 | |
|       b->sel_foreground = GetSysColor(COLOR_HIGHLIGHTTEXT);
 | |
|       b->sel_background = GetSysColor(COLOR_HIGHLIGHT);
 | |
|       return 0;
 | |
| 
 | |
|     case WM_RLC_WRITE:
 | |
|     { int count = (int)wParam;
 | |
|       TCHAR *buf = (TCHAR *)lParam;
 | |
| 
 | |
|       if ( OQSIZE - b->output_queued > count )
 | |
|       { _tcsncpy(&b->output_queue[b->output_queued], buf, count);
 | |
| 	b->output_queued += count;
 | |
|       } else
 | |
|       { if ( b->output_queued > 0 )
 | |
| 	  rlc_flush_output(b);
 | |
| 
 | |
| 	if ( count <= OQSIZE )
 | |
| 	{ _tcsncpy(b->output_queue, buf, count);
 | |
| 	  b->output_queued = count;
 | |
| 	} else
 | |
| 	  rlc_do_write(b, buf, count);
 | |
|       }
 | |
| 
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     case WM_RLC_FLUSH:
 | |
|     { rlc_flush_output(b);
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     case WM_RLC_MENU:
 | |
|     { rlc_menu_action((rlc_console) b, (struct menu_data*)lParam);
 | |
| 
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     case WM_RLC_CLOSEWIN:
 | |
|       return 0;
 | |
| 
 | |
|     case WM_CLOSE:
 | |
|       if ( rlc_kill(b) )
 | |
|         return 0;
 | |
|       break;
 | |
| 
 | |
|     case WM_DESTROY:
 | |
|       b->window = NULL;
 | |
|       PostQuitMessage(0);
 | |
|       return 0;
 | |
|   }
 | |
| 
 | |
|   return DefWindowProc(hwnd, message, wParam, lParam);
 | |
| }
 | |
| 
 | |
| static int
 | |
| rlc_get_message(MSG *msg, HWND hwnd, UINT low, UINT high)
 | |
| { int rc;
 | |
| again:
 | |
|   if ( (rc=GetMessage(msg, hwnd, low, high)) )
 | |
|   { if ( _rlc_message_hook &&
 | |
| 	 (*_rlc_message_hook)(msg->hwnd, msg->message,
 | |
| 			      msg->wParam, msg->lParam) )
 | |
|       goto again;
 | |
|   }
 | |
| 
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_dispatch(RlcData b)
 | |
| { MSG msg;
 | |
| 
 | |
|   if ( rlc_get_message(&msg, NULL, 0, 0) && msg.message != WM_RLC_CLOSEWIN )
 | |
|   { /* DEBUG(Dprintf("Thread %x got message 0x%04x\n",
 | |
| 		     GetCurrentThreadId(), msg.message));
 | |
|     */
 | |
|     TranslateMessage(&msg);
 | |
|     DispatchMessage(&msg);
 | |
|     rlc_flush_output(b);
 | |
|     return;
 | |
|   } else
 | |
|   { DEBUG(Dprintf(_T("Thread %x got WM_RLC_CLOSEWIN\n"),
 | |
| 		  GetCurrentThreadId()));
 | |
|     b->queue->flags |= RLC_EOF;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| rlc_yield()
 | |
| { MSG msg;
 | |
| 
 | |
|   while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
 | |
|   { TranslateMessage(&msg);
 | |
|     DispatchMessage(&msg);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	 CHARACTER TYPES	*
 | |
| 		 *******************************/
 | |
| 
 | |
| static void
 | |
| rlc_init_word_chars(void)
 | |
| { int i;
 | |
| 
 | |
|   for(i=0; i<CHAR_MAX; i++)
 | |
|     _rlc_word_chars[i] = (isalnum(i) || i == '_') ? TRUE : FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| rlc_word_char(int chr, int isword)
 | |
| { if ( chr > 0 && chr < CHAR_MAX )
 | |
|     _rlc_word_chars[chr] = isword;
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| rlc_is_word_char(int chr)
 | |
| { if ( chr > 0 && chr < CHAR_MAX )
 | |
|     return _rlc_word_chars[chr];
 | |
| 
 | |
|   return _istalnum((wint_t)chr);	/* only UNICODE version */
 | |
| }
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	    SELECTION		*
 | |
| 		 *******************************/
 | |
| 
 | |
| #define SelLT(l1, c1, l2, c2) ((l1) < (l2) || ((l1) == (l2) && (c1) < (c2)))
 | |
| #define SelEQ(l1, c1, l2, c2) ((l1) == (l2) && (c1) == (c2))
 | |
| 
 | |
| static int
 | |
| rlc_min(RlcData b, int x, int y)
 | |
| { if ( rlc_count_lines(b, b->first, x) < rlc_count_lines(b, b->first, y) )
 | |
|     return x;
 | |
| 
 | |
|   return y;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| rlc_max(RlcData b, int x, int y)
 | |
| { if ( rlc_count_lines(b, b->first, x) > rlc_count_lines(b, b->first, y) )
 | |
|     return x;
 | |
| 
 | |
|   return y;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_changed_line(RlcData b, int i, int mask)
 | |
| { b->lines[i].changed |= mask;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_set_selection(RlcData b, int sl, int sc, int el, int ec)
 | |
| { int sch = rlc_min(b, sl, b->sel_start_line);
 | |
|   int ech = rlc_max(b, el, b->sel_end_line);
 | |
|   int nel = NextLine(b, el);
 | |
|   int nsel= NextLine(b, b->sel_end_line);
 | |
|   int i;
 | |
|   int innow  = FALSE;
 | |
|   int insoon = FALSE;
 | |
| 
 | |
| 					/* find the lines that changed */
 | |
|   for(i=sch; ; i = NextLine(b, i))
 | |
|   { if ( i == sl )
 | |
|     { insoon = TRUE;
 | |
|       if ( i == b->sel_start_line )
 | |
|       { innow = TRUE;
 | |
| 	if ( sc != b->sel_start_char ||
 | |
| 	     (i == el && i != b->sel_end_line) ||
 | |
| 	     (i == b->sel_end_line && i != el) )
 | |
| 	  rlc_changed_line(b, i, CHG_CHANGED);
 | |
|       } else
 | |
| 	rlc_changed_line(b, i, CHG_CHANGED);
 | |
|     } else if ( i == b->sel_start_line )
 | |
|     { innow = TRUE;
 | |
|       rlc_changed_line(b, i, CHG_CHANGED);
 | |
|     }
 | |
| 
 | |
|     if ( i == b->sel_end_line )
 | |
|     { if ( (i == el && ec != b->sel_end_char) || el != i )
 | |
| 	rlc_changed_line(b, i, CHG_CHANGED);
 | |
|     }
 | |
| 
 | |
|     if ( innow != insoon )
 | |
|       rlc_changed_line(b, i, CHG_CHANGED);
 | |
| 
 | |
|     if ( i == nel )
 | |
|     { insoon = FALSE;
 | |
|       if ( i == nsel )
 | |
| 	innow = FALSE;
 | |
|       else
 | |
| 	rlc_changed_line(b, i, CHG_CHANGED);
 | |
|     } else if ( i == nsel )
 | |
|     { innow = FALSE;
 | |
|       rlc_changed_line(b, i, CHG_CHANGED);
 | |
|     }
 | |
| 
 | |
|     if ( i == ech )
 | |
|       break;
 | |
|   }
 | |
| 
 | |
| 					/* update the attributes */
 | |
|   b->sel_start_line = sl;
 | |
|   b->sel_start_char = sc;
 | |
|   b->sel_end_line   = el;
 | |
|   b->sel_end_char   = ec;
 | |
| 
 | |
| 					/* ... and request a repaint */
 | |
|   rlc_request_redraw(b);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_translate_mouse(RlcData b, int x, int y, int *line, int *chr)
 | |
| { int ln = b->window_start;
 | |
|   int n = b->window_size;		/* # lines */
 | |
|   TextLine tl;
 | |
|   x-= b->cw;				/* margin */
 | |
| 
 | |
|   if ( !b->window )
 | |
|     return;
 | |
| 
 | |
|   while( y > b->ch && ln != b->last && n-- > 0 )
 | |
|   { ln = NextLine(b, ln);
 | |
|     y -= b->ch;
 | |
|   }
 | |
|   *line = ln;
 | |
|   tl = &b->lines[ln];
 | |
| 
 | |
|   if ( b->fixedfont )
 | |
|   { *chr = min(x/b->cw, tl->size);
 | |
|   } else if ( tl->size == 0 )
 | |
|   { *chr = 0;
 | |
|   } else
 | |
|   { text_char *s = tl->text;
 | |
|     HDC hdc = GetDC(b->window);
 | |
|     int f = 0;
 | |
|     int t = tl->size;
 | |
|     int m = (f+t)/2;
 | |
|     int i;
 | |
| 
 | |
|     SelectObject(hdc, b->hfont);
 | |
| 
 | |
|     for(i=10; --i > 0; m=(f+t)/2)
 | |
|     { int w;
 | |
| 
 | |
|       w = text_width(b, hdc, s, m);
 | |
|       if ( x > w )
 | |
|       { int cw;
 | |
| 
 | |
| 	GetCharWidth32(hdc, s[m].code, s[m].code, &cw);
 | |
| 	if ( x < w+cw )
 | |
| 	{ *chr = m;
 | |
| 	  return;
 | |
| 	}
 | |
| 	f = m+1;
 | |
|       } else
 | |
|       { t = m;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     *chr = m;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_start_selection(RlcData b, int x, int y)
 | |
| { int l, c;
 | |
| 
 | |
|   rlc_translate_mouse(b, x, y, &l, &c);
 | |
|   b->sel_unit = SEL_CHAR;
 | |
|   b->sel_org_line = l;
 | |
|   b->sel_org_char = c;
 | |
|   rlc_set_selection(b, l, c, l, c);
 | |
| }
 | |
| 
 | |
| 
 | |
| static int				/* v >= f && v <= t */
 | |
| rlc_between(RlcData b, int f, int t, int v)
 | |
| { int h = rlc_count_lines(b, b->first, v);
 | |
| 
 | |
|   if ( h >= rlc_count_lines(b, b->first, f) &&
 | |
|        h <= rlc_count_lines(b, b->first, t) )
 | |
|     return TRUE;
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_word_selection(RlcData b, int x, int y)
 | |
| { int l, c;
 | |
| 
 | |
|   rlc_translate_mouse(b, x, y, &l, &c);
 | |
|   if ( rlc_between(b, b->first, b->last, l) )
 | |
|   { TextLine tl = &b->lines[l];
 | |
| 
 | |
|     if ( c < tl->size && rlc_is_word_char(tl->text[c].code) )
 | |
|     { int f, t;
 | |
| 
 | |
|       for(f=c; f>0 && rlc_is_word_char(tl->text[f-1].code); f--)
 | |
| 	;
 | |
|       for(t=c; t<tl->size && rlc_is_word_char(tl->text[t].code); t++)
 | |
| 	;
 | |
|       rlc_set_selection(b, l, f, l, t);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   b->sel_unit = SEL_WORD;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_extend_selection(RlcData b, int x, int y)
 | |
| { int l, c;
 | |
|   int el = b->sel_org_line;
 | |
|   int ec = b->sel_org_char;
 | |
| 
 | |
|   rlc_translate_mouse(b, x, y, &l, &c);
 | |
|   if ( SelLT(l, c, b->sel_org_line, b->sel_org_char) )
 | |
|   { if ( b->sel_unit == SEL_WORD )
 | |
|     { if ( rlc_between(b, b->first, b->last, l) )
 | |
|       { TextLine tl = &b->lines[l];
 | |
| 
 | |
| 	if ( c < tl->size && rlc_is_word_char(tl->text[c].code) )
 | |
| 	  for(; c > 0 && rlc_is_word_char(tl->text[c-1].code); c--)
 | |
| 	    ;
 | |
|       }
 | |
|       if ( rlc_between(b, b->first, b->last, el) )
 | |
|       { TextLine tl = &b->lines[el];
 | |
| 
 | |
| 	if ( ec < tl->size && rlc_is_word_char(tl->text[ec].code) )
 | |
| 	  for(; ec < tl->size && rlc_is_word_char(tl->text[ec].code); ec++)
 | |
| 	    ;
 | |
|       }
 | |
|     } else if ( b->sel_unit == SEL_LINE )
 | |
|       c = 0;
 | |
|     rlc_set_selection(b, l, c, el, ec);
 | |
|   } else if ( SelLT(b->sel_org_line, b->sel_org_char, l, c) )
 | |
|   { if ( b->sel_unit == SEL_WORD )
 | |
|     { if ( rlc_between(b, b->first, b->last, l) )
 | |
|       { TextLine tl = &b->lines[l];
 | |
| 
 | |
| 	if ( c < tl->size && rlc_is_word_char(tl->text[c].code) )
 | |
| 	  for(; c < tl->size && rlc_is_word_char(tl->text[c].code); c++)
 | |
| 	    ;
 | |
|       }
 | |
|       if ( rlc_between(b, b->first, b->last, el) )
 | |
|       { TextLine tl = &b->lines[el];
 | |
| 
 | |
| 	if ( ec < tl->size && rlc_is_word_char(tl->text[ec].code) )
 | |
| 	  for(; ec > 0 && rlc_is_word_char(tl->text[ec-1].code); ec--)
 | |
| 	    ;
 | |
|       }
 | |
|     } else if ( b->sel_unit == SEL_LINE )
 | |
|       c = b->width;
 | |
|     rlc_set_selection(b, el, ec, l, c);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static TCHAR *
 | |
| rlc_read_from_window(RlcData b, int sl, int sc, int el, int ec)
 | |
| { int bufsize = 256;
 | |
|   TCHAR *buf;
 | |
|   int i = 0;
 | |
| 
 | |
|   if ( el < sl || (el == sl && ec < sc) )
 | |
|     return NULL;			/* invalid region */
 | |
|   if ( !(buf = rlc_malloc(bufsize * sizeof(TCHAR))) )
 | |
|     return NULL;			/* not enough memory */
 | |
| 
 | |
|   for( ; ; sc = 0, sl = NextLine(b, sl))
 | |
|   { TextLine tl = &b->lines[sl];
 | |
|     if ( tl )
 | |
|     { int e = (sl == el ? ec : tl->size);
 | |
| 
 | |
|       if ( e > tl->size )
 | |
| 	e = tl->size;
 | |
| 
 | |
|       while(sc < e)
 | |
|       { if ( i+1 >= bufsize )
 | |
| 	{ bufsize *= 2;
 | |
| 	  if ( !(buf = rlc_realloc(buf, bufsize * sizeof(TCHAR))) )
 | |
| 	    return NULL;		/* not enough memory */
 | |
| 	}
 | |
| 	buf[i++] = tl->text[sc++].code;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if ( sl == el || sl == b->last )
 | |
|     { buf[i++] = '\0';			/* Always room for the 0 */
 | |
|       return buf;
 | |
|     }
 | |
| 
 | |
|     if ( tl && !tl->softreturn )
 | |
|     { if ( i+2 >= bufsize )
 | |
|       { bufsize *= 2;
 | |
| 	if ( !(buf = rlc_realloc(buf, bufsize * sizeof(TCHAR))) )
 | |
| 	  return NULL;			/* not enough memory */
 | |
|       }
 | |
|       buf[i++] = '\r';			/* Bill ... */
 | |
|       buf[i++] = '\n';
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| rlc_has_selection(RlcData b)
 | |
| { if ( SelEQ(b->sel_start_line, b->sel_start_char,
 | |
| 	     b->sel_end_line,   b->sel_end_char) )
 | |
|     return FALSE;
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static TCHAR *
 | |
| rlc_selection(RlcData b)
 | |
| { if ( rlc_has_selection(b) )
 | |
|     return rlc_read_from_window(b,
 | |
| 				b->sel_start_line, b->sel_start_char,
 | |
| 				b->sel_end_line,   b->sel_end_char);
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_copy(RlcData b)
 | |
| { TCHAR *sel = rlc_selection(b);
 | |
| 
 | |
|   if ( sel && b->window )
 | |
|   { size_t size = _tcslen(sel);
 | |
|     HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (size + 1)*sizeof(TCHAR));
 | |
|     TCHAR far *data;
 | |
|     size_t i;
 | |
| 
 | |
|     if ( !mem )
 | |
|     { MessageBox(NULL, _T("Not enough memory to copy"), _T("Error"), MB_OK);
 | |
|       return;
 | |
|     }
 | |
|     data = GlobalLock(mem);
 | |
| 
 | |
|     for(i=0; i<size; i++)
 | |
|       *data++ = sel[i];
 | |
|     *data = '\0';
 | |
| 
 | |
|     GlobalUnlock(mem);
 | |
|     OpenClipboard(b->window);
 | |
|     EmptyClipboard();
 | |
| #ifdef UNICODE
 | |
|     SetClipboardData(CF_UNICODETEXT, mem);
 | |
| #else
 | |
|     SetClipboardData(CF_TEXT, mem);
 | |
| #endif
 | |
|     CloseClipboard();
 | |
| 
 | |
|     rlc_free(sel);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *           REPAINT		*
 | |
| 		 *******************************/
 | |
| 
 | |
| static void
 | |
| rlc_place_caret(RlcData b)
 | |
| { if ( b->has_focus && b->window )
 | |
|   { int line = rlc_count_lines(b, b->window_start, b->caret_y);
 | |
| 
 | |
|     if ( line < b->window_size )
 | |
|     { if ( b->fixedfont )
 | |
|       { SetCaretPos((b->caret_x + 1) * b->cw, line * b->ch);
 | |
|       } else
 | |
|       { HDC hdc = GetDC(b->window);
 | |
| 	int tw;
 | |
| 	TextLine tl = &b->lines[b->caret_y];
 | |
| 	HFONT old;
 | |
| 
 | |
| 	old = SelectObject(hdc, b->hfont);
 | |
| 	tw = text_width(b, hdc, tl->text, b->caret_x);
 | |
| 	SelectObject(hdc, old);
 | |
| 	ReleaseDC(b->window, hdc);
 | |
| 
 | |
| 	SetCaretPos(b->cw + tw, line * b->ch);
 | |
|       }
 | |
|       if ( !b->caret_is_shown )
 | |
|       { ShowCaret(b->window);
 | |
| 	b->caret_is_shown = TRUE;
 | |
| 
 | |
| 	return;
 | |
|       }
 | |
|     } else
 | |
|     { if ( b->caret_is_shown == TRUE )
 | |
|       { HideCaret(b->window);
 | |
| 	b->caret_is_shown = FALSE;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   b->caret_is_shown = FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_update_scrollbar(RlcData b)
 | |
| { if ( b->window )
 | |
|   { int nsb_lines = rlc_count_lines(b, b->first, b->last);
 | |
|     int nsb_start = rlc_count_lines(b, b->first, b->window_start);
 | |
| 
 | |
|     if ( nsb_lines != b->sb_lines ||
 | |
| 	 nsb_start != b->sb_start )
 | |
|     { SetScrollRange(b->window, SB_VERT, 0, nsb_lines, FALSE);
 | |
|       SetScrollPos(  b->window, SB_VERT, nsb_start, TRUE);
 | |
| 
 | |
|       b->sb_lines = nsb_lines;
 | |
|       b->sb_start = nsb_start;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rcl_paint_text(RlcData b, HDC hdc,
 | |
| 	       TextLine tl, int from, int to,
 | |
| 	       int ty, int *cx, int insel)
 | |
| { text_char *chars, *s;
 | |
|   text_char buf[MAXLINE];
 | |
|   TCHAR text[MAXLINE];
 | |
|   TCHAR *t;
 | |
|   int len = to-from;
 | |
|   int i;
 | |
| 
 | |
|   if ( len <= 0 )
 | |
|     return;
 | |
| 
 | |
|   if ( tl->text && to <= tl->size )
 | |
|   { chars = &tl->text[from];
 | |
|   } else
 | |
|   { text_char *o;
 | |
|     int copy;
 | |
| 
 | |
|     o = chars = buf;
 | |
|     s = &tl->text[from];
 | |
|     copy = tl->text ? tl->size-from : 0;
 | |
|     for(i=0; i<copy; i++)
 | |
|       *o++ = *s++;
 | |
|     for(; i<len; i++, o++)
 | |
|     { o->code = ' ';
 | |
|       o->flags = TF_DEFAULT;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   for(t=text, s=chars, i=0; i < len; i++, t++, s++)
 | |
|     *t = s->code;
 | |
| 
 | |
|   if ( insel )					/* TBD: Cache */
 | |
|   { SetBkColor(hdc, b->sel_background);
 | |
|     SetTextColor(hdc, b->sel_foreground);
 | |
|     TextOut(hdc, *cx, ty, text, len);
 | |
|     *cx += tchar_width(b, hdc, text, len);
 | |
|   } else
 | |
|   { int start, segment;
 | |
| 
 | |
|     for(start=0, s=chars, t=text;
 | |
| 	start<len;
 | |
| 	start+=segment, s+=segment, t+=segment)
 | |
|     { text_flags flags = s->flags;
 | |
|       int left = len-start;
 | |
| 
 | |
|       for(segment=0; s[segment].flags == flags && segment<left; segment++)
 | |
| 	;
 | |
| 
 | |
|       if ( TF_FG(flags) == ANSI_COLOR_DEFAULT )
 | |
| 	SetTextColor(hdc, b->foreground);
 | |
|       else
 | |
| 	SetTextColor(hdc, b->ansi_color[TF_FG(flags)]);
 | |
| 
 | |
|       if ( TF_BG(flags) == ANSI_COLOR_DEFAULT )
 | |
| 	SetBkColor(hdc, b->background);
 | |
|       else
 | |
| 	SetBkColor(hdc, b->ansi_color[TF_BG(flags)]);
 | |
| 
 | |
|       TextOut(hdc, *cx, ty, t, segment);
 | |
|       if ( TF_BOLD(flags) )
 | |
|       { SetBkMode(hdc, TRANSPARENT);
 | |
| 	TextOut(hdc, (*cx)+1, ty, t, segment);
 | |
| 	TextOut(hdc, *cx, ty+1, t, segment);
 | |
| 	SetBkMode(hdc, OPAQUE);
 | |
|       }
 | |
|       *cx += tchar_width(b, hdc, t, segment);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_redraw(RlcData b)
 | |
| { PAINTSTRUCT ps;
 | |
|   HDC hdc = BeginPaint(b->window, &ps);
 | |
|   int sl = max(0, ps.rcPaint.top/b->ch);
 | |
|   int el = min(b->window_size, ps.rcPaint.bottom/b->ch);
 | |
|   int l = rlc_add_lines(b, b->window_start, sl);
 | |
|   int pl = sl;				/* physical line */
 | |
|   RECT rect;
 | |
|   HBRUSH bg;
 | |
|   int stockbg;
 | |
|   int insel = FALSE;			/* selected lines? */
 | |
| 
 | |
|   SelectObject(hdc, b->hfont);
 | |
|   SetTextColor(hdc, b->foreground);
 | |
|   SetBkColor(hdc, b->background);
 | |
| 
 | |
|   if ( b->background == RGB(255, 255, 255) )
 | |
|   { bg = GetStockObject(WHITE_BRUSH);
 | |
|     stockbg = TRUE;
 | |
|   } else
 | |
|   { bg = CreateSolidBrush(b->background);
 | |
|     stockbg = FALSE;
 | |
|   }
 | |
| 
 | |
|   if ( b->has_focus && b->caret_is_shown )
 | |
|   { HideCaret(b->window);
 | |
|     b->caret_is_shown = FALSE;
 | |
|   }
 | |
| 
 | |
|   if ( rlc_count_lines(b, b->first, b->sel_start_line) <
 | |
|        rlc_count_lines(b, b->first, l) &&
 | |
|        rlc_count_lines(b, b->first, b->sel_end_line) >=
 | |
|        rlc_count_lines(b, b->first, l) )
 | |
|     insel = TRUE;
 | |
| 
 | |
|   if ( insel )
 | |
|   { SetBkColor(hdc, b->sel_background);
 | |
|     SetTextColor(hdc, b->sel_foreground);
 | |
|   }
 | |
| 
 | |
|   for(; pl <= el; l = NextLine(b, l), pl++)
 | |
|   { TextLine tl = &b->lines[l];
 | |
|     int ty = b->ch * pl;
 | |
|     int cx = b->cw;
 | |
| 
 | |
|     rect.top    = ty;
 | |
|     rect.bottom = rect.top + b->ch;
 | |
| 
 | |
| 					/* compute selection */
 | |
|     if ( l == b->sel_start_line )
 | |
|     { int cf = b->sel_start_char;
 | |
|       int ce = (b->sel_end_line != b->sel_start_line ? b->width
 | |
| 						     : b->sel_end_char);
 | |
| 
 | |
|       rcl_paint_text(b, hdc, tl,  0, cf, ty, &cx, insel);
 | |
|       insel = TRUE;
 | |
|       rcl_paint_text(b, hdc, tl, cf, ce, ty, &cx, insel);
 | |
|       if ( l == b->sel_end_line )
 | |
|       { insel = FALSE;
 | |
| 	rcl_paint_text(b, hdc, tl, ce, b->width, ty, &cx, insel);
 | |
|       } else
 | |
| 	insel = TRUE;
 | |
|     } else if ( l == b->sel_end_line )	/* end of selection */
 | |
|     { int ce = b->sel_end_char;
 | |
| 
 | |
|       rcl_paint_text(b, hdc, tl, 0, ce, ty, &cx, insel);
 | |
|       insel = FALSE;
 | |
|       rcl_paint_text(b, hdc, tl, ce, b->width, ty, &cx, insel);
 | |
|     } else				/* entire line in/out selection */
 | |
|     { rcl_paint_text(b, hdc, tl, 0, b->width, ty, &cx, insel);
 | |
|     }
 | |
| 
 | |
| 					/* clear remainder of line */
 | |
|     if ( cx < b->width * (b->cw+1) )
 | |
|     { rect.left   = cx;
 | |
|       rect.right  = b->width * (b->cw+1);
 | |
|       rect.top    = b->ch * pl;
 | |
|       rect.bottom = rect.top + b->ch;
 | |
|       FillRect(hdc, &rect, bg);
 | |
|     }
 | |
| 
 | |
|     tl->changed = CHG_RESET;
 | |
| 
 | |
|     if ( l == b->last )			/* clear to end of window */
 | |
|     { rect.left   = b->cw;
 | |
|       rect.right  = b->width * (b->cw+1);
 | |
|       rect.top    = b->ch * (pl+1);
 | |
|       rect.bottom = b->ch * (el+1);
 | |
|       FillRect(hdc, &rect, bg);
 | |
| 
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
|   rlc_place_caret(b);
 | |
| 
 | |
|   b->changed = CHG_RESET;
 | |
|   if ( !stockbg )
 | |
|     DeleteObject(bg);
 | |
| 
 | |
|   EndPaint(b->window, &ps);
 | |
| 
 | |
|   rlc_update_scrollbar(b);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_request_redraw(RlcData b)
 | |
| { if ( b->changed & CHG_CHANGED )
 | |
|   { if ( b->window )
 | |
|       InvalidateRect(b->window, NULL, FALSE);
 | |
|   } else
 | |
|   { int i = b->window_start;
 | |
|     int y = 0;
 | |
|     RECT rect;
 | |
|     int first = TRUE;
 | |
| 
 | |
|     rect.left = b->cw;
 | |
|     rect.right = (b->width+1) * b->cw;
 | |
| 
 | |
|     for(; y < b->window_size; y++, i = NextLine(b, i))
 | |
|     { TextLine l = &b->lines[i];
 | |
| 
 | |
|       if ( l->changed & CHG_CHANGED )
 | |
|       { if ( first )
 | |
| 	{ rect.top = y * b->ch;
 | |
| 	  rect.bottom = rect.top + b->ch;
 | |
| 	  first = FALSE;
 | |
| 	} else
 | |
| 	  rect.bottom = (y+1) * b->ch;
 | |
|       }
 | |
|       if ( i == b->last )
 | |
| 	break;
 | |
|     }
 | |
| 
 | |
|     if ( !first && b->window )
 | |
|       InvalidateRect(b->window, &rect, FALSE);
 | |
|     else if ( b->changed & CHG_CARET )
 | |
|       rlc_place_caret(b);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_normalise(RlcData b)
 | |
| { if ( rlc_count_lines(b, b->window_start, b->caret_y) >= b->window_size )
 | |
|   { b->window_start = rlc_add_lines(b, b->caret_y, -(b->window_size-1));
 | |
|     b->changed |= CHG_CARET|CHG_CLEAR|CHG_CHANGED;
 | |
|     rlc_request_redraw(b);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_resize_pixel_units(RlcData b, int w, int h)
 | |
| { int nw = max(20, w/b->cw)-2;		/* 1 character space for margins */
 | |
|   int nh = max(1, h/b->ch);
 | |
| 
 | |
|   DEBUG(Dprintf(_T("rlc_resize_pixel_units(%p, %d, %d) (%dx%d)\n"),
 | |
| 		b, w, h, nw, nh));
 | |
| 
 | |
|   if ( b->width == nw && b->window_size == nh )
 | |
|     return;				/* no real change */
 | |
| 
 | |
|   rlc_resize(b, nw, nh);
 | |
| 
 | |
|   if ( _rlc_resize_hook )
 | |
|     (*_rlc_resize_hook)(b->width, b->window_size);
 | |
|   else
 | |
|   {
 | |
| #ifdef SIGWINCH
 | |
|     raise(SIGWINCH);
 | |
| #endif
 | |
|   }
 | |
| 
 | |
|   rlc_request_redraw(b);
 | |
| }
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	       FONT		*
 | |
| 		 *******************************/
 | |
| 
 | |
| static void
 | |
| rlc_init_text_dimensions(RlcData b, HFONT font)
 | |
| { HDC hdc;
 | |
|   TEXTMETRIC tm;
 | |
| 
 | |
|   if ( font )
 | |
|   { b->hfont = font;
 | |
|   } else if ( b->create_attributes )
 | |
|   { rlc_console_attr *a = b->create_attributes;
 | |
|     if ( !a->face_name[0] )
 | |
|       b->hfont = GetStockObject(ANSI_FIXED_FONT);
 | |
|     else
 | |
|     { LOGFONT lfont;
 | |
| 
 | |
|       memset(&lfont, 0, sizeof(lfont));
 | |
| 
 | |
|       lfont.lfHeight          = a->font_size;
 | |
|       lfont.lfWeight          = a->font_weight;
 | |
|       lfont.lfPitchAndFamily  = a->font_family;
 | |
|       lfont.lfCharSet	      = a->font_char_set;
 | |
|       _tcsncpy(lfont.lfFaceName, a->face_name, 31);
 | |
| 
 | |
|       if ( !(b->hfont = CreateFontIndirect(&lfont)) )
 | |
| 	b->hfont = GetStockObject(ANSI_FIXED_FONT);
 | |
|     }
 | |
|   } else
 | |
|     b->hfont = GetStockObject(ANSI_FIXED_FONT);
 | |
| 
 | |
| 					/* test for fixed?*/
 | |
|   hdc = GetDC(NULL);
 | |
|   SelectObject(hdc, b->hfont);
 | |
|   GetTextMetrics(hdc, &tm);
 | |
|   b->cw = tm.tmAveCharWidth;
 | |
|   b->cb = tm.tmHeight;
 | |
|   b->ch = tm.tmHeight + tm.tmExternalLeading;
 | |
|   b->fixedfont = (tm.tmPitchAndFamily & TMPF_FIXED_PITCH ? FALSE : TRUE);
 | |
|   ReleaseDC(NULL, hdc);
 | |
| 
 | |
|   if ( b->window )
 | |
|   { RECT rect;
 | |
| 
 | |
|     if ( b->has_focus == TRUE )
 | |
|     { CreateCaret(b->window, NULL, b->fixedfont ? b->cw : 3, b->ch-1);
 | |
|       rlc_place_caret(b);
 | |
|     }
 | |
| 
 | |
|     GetClientRect(b->window, &rect);
 | |
|     rlc_resize_pixel_units(b, rect.right - rect.left, rect.bottom - rect.top);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| text_width(RlcData b, HDC hdc, const text_char *text, int len)
 | |
| { if ( b->fixedfont )
 | |
|   { return len * b->cw;
 | |
|   } else
 | |
|   { SIZE size;
 | |
|     TCHAR tmp[MAXLINE];
 | |
|     int i;
 | |
| 
 | |
|     for(i=0; i<len; i++)
 | |
|       tmp[i] = text[i].code;
 | |
| 
 | |
|     GetTextExtentPoint32(hdc, tmp, len, &size);
 | |
|     return size.cx;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| tchar_width(RlcData b, HDC hdc, const TCHAR *text, int len)
 | |
| { if ( b->fixedfont )
 | |
|   { return len * b->cw;
 | |
|   } else
 | |
|   { SIZE size;
 | |
| 
 | |
|     GetTextExtentPoint32(hdc, text, len, &size);
 | |
|     return size.cx;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_save_font_options(HFONT font, rlc_console_attr *attr)
 | |
| { if ( font == GetStockObject(ANSI_FIXED_FONT) )
 | |
|   { attr->face_name[0] = '\0';
 | |
|   } else
 | |
|   { LOGFONT lf;
 | |
| 
 | |
|     if ( GetObject(font, sizeof(lf), &lf) )
 | |
|     { memcpy(attr->face_name, lf.lfFaceName, sizeof(attr->face_name)-1);
 | |
| 
 | |
|       attr->font_family   = lf.lfPitchAndFamily;
 | |
|       attr->font_size     = lf.lfHeight;
 | |
|       attr->font_weight   = lf.lfWeight;
 | |
|       attr->font_char_set = lf.lfCharSet;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	   FONT SELECTION	*
 | |
| 		 *******************************/
 | |
| 
 | |
| static void
 | |
| rlc_queryfont(RlcData b)
 | |
| { CHOOSEFONT cf;
 | |
|   LOGFONT lf;
 | |
| 
 | |
|   memset(&cf, 0, sizeof(cf));
 | |
|   memset(&lf, 0, sizeof(lf));
 | |
| 
 | |
|   lf.lfHeight          = 16;
 | |
|   lf.lfWeight          = FW_NORMAL;
 | |
|   lf.lfPitchAndFamily  = FIXED_PITCH|FF_MODERN;
 | |
| 
 | |
|   cf.lStructSize = sizeof(cf);
 | |
|   cf.hwndOwner   = b->window;
 | |
|   cf.lpLogFont   = &lf;
 | |
|   cf.Flags       = CF_SCREENFONTS|
 | |
| 		   CF_NOVERTFONTS|
 | |
| 		   CF_NOSIMULATIONS|
 | |
| 		   CF_FORCEFONTEXIST|
 | |
| 		   CF_INITTOLOGFONTSTRUCT;
 | |
|   cf.nFontType   = SCREEN_FONTTYPE;
 | |
| 
 | |
|   if ( ChooseFont(&cf) )
 | |
|   { HFONT f;
 | |
|     if ( (f = CreateFontIndirect(&lf)) )
 | |
|     { rlc_init_text_dimensions(b, f);
 | |
| 
 | |
|       InvalidateRect(b->window, NULL, TRUE);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *     BUFFER INITIALISATION	*
 | |
| 		 *******************************/
 | |
| 
 | |
| static RlcData
 | |
| rlc_make_buffer(int w, int h)
 | |
| { RlcData b = rlc_malloc(sizeof(rlc_data));
 | |
|   int i;
 | |
| 
 | |
|   memset(b, 0, sizeof(*b));
 | |
|   b->magic = RLC_MAGIC;
 | |
| 
 | |
|   b->height         = h;
 | |
|   b->width          = w;
 | |
|   b->window_size    = 25;
 | |
|   b->lines          = rlc_malloc(sizeof(text_line) * h);
 | |
|   b->cmdstat	    = CMD_INITIAL;
 | |
|   b->changed	    = CHG_CARET|CHG_CHANGED|CHG_CLEAR;
 | |
|   b->imode	    = IMODE_COOKED;	/* switch on first rlc_read() call */
 | |
|   b->imodeswitch    = FALSE;
 | |
|   b->lhead	    = NULL;
 | |
|   b->ltail	    = NULL;
 | |
| 
 | |
|   memset(b->lines, 0, sizeof(text_line) * h);
 | |
|   for(i=0; i<h; i++)
 | |
|     b->lines[i].adjusted = TRUE;
 | |
| 
 | |
|   rlc_init_word_chars();
 | |
| 
 | |
|   return b;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| Copy all lines one `back' (i.e.  towards   older  lines).  If the oldest
 | |
| (first) line is adjacent to the last, throw it away.
 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| static void
 | |
| rlc_shift_lines_down(RlcData b, int line)
 | |
| { int i = b->first;
 | |
|   int p = PrevLine(b, i);
 | |
| 
 | |
|   if ( p != b->last )			/* move first (oldest line) */
 | |
|   { b->first = p;
 | |
|     b->lines[p] = b->lines[i];
 | |
|   } else				/* delete first (oldest) line */
 | |
|     rlc_free_line(b, b->first);
 | |
| 					/* copy the lines */
 | |
|   for(p=i, i = NextLine(b, i); p != line; p=i, i = NextLine(b, i))
 | |
|     b->lines[p] = b->lines[i];
 | |
| 
 | |
|   b->lines[line].text       = NULL;	/* make this one `free' */
 | |
|   b->lines[line].size       = 0;
 | |
|   b->lines[line].adjusted   = TRUE;
 | |
|   b->lines[line].softreturn = FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_shift_lines_up(RlcData b, int line)
 | |
| { int prev = PrevLine(b, line);
 | |
| 
 | |
|   while(line != b->first)
 | |
|   { b->lines[line] = b->lines[prev];
 | |
|     line = prev;
 | |
|     prev = PrevLine(b, prev);
 | |
|   }
 | |
| 
 | |
|   rlc_reinit_line(b, b->first);
 | |
|   b->first = NextLine(b, b->first);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_resize(RlcData b, int w, int h)
 | |
| { int i;
 | |
| 
 | |
|   if ( b->width == w && b->window_size == h )
 | |
|     return;				/* no real change */
 | |
| 
 | |
|   DEBUG(Dprintf(_T("Resizing %dx%d --> %dx%d\n"),
 | |
| 		b->width, b->window_size, w, h));
 | |
| 
 | |
|   b->window_size = h;
 | |
|   b->width = w;
 | |
| 
 | |
|   for(i = b->first; /*i != b->last*/; i = NextLine(b, i))
 | |
|   { TextLine tl = &b->lines[i];
 | |
| 
 | |
|     if ( tl->text && tl->adjusted == FALSE )
 | |
|       rlc_adjust_line(b, i);
 | |
| 
 | |
|     if ( tl->size > w )
 | |
|     { if ( !tl->softreturn )		/* hard --> soft */
 | |
|       { TextLine pl;
 | |
| 
 | |
| 	rlc_shift_lines_down(b, i);
 | |
| 	DEBUG(Dprint_lines(b, b->first, b->first));
 | |
| 	DEBUG(Dprintf(_T("b->first = %d, b->last = %d\n"), b->first, b->last));
 | |
| 	pl = &b->lines[PrevLine(b, i)];	/* this is the moved line */
 | |
| 	tl->text = rlc_malloc((pl->size - w)*sizeof(text_char));
 | |
| 	memmove(tl->text, &pl->text[w], (pl->size - w)*sizeof(text_char));
 | |
| 	DEBUG(Dprintf(_T("Copied %d chars from line %d to %d\n"),
 | |
| 		      pl->size - w, pl - b->lines, i));
 | |
| 	tl->size = pl->size - w;
 | |
| 	tl->adjusted = TRUE;
 | |
| 	tl->softreturn = FALSE;
 | |
| 	pl->softreturn = TRUE;
 | |
| 	pl->text = rlc_realloc(pl->text, w * sizeof(text_char));
 | |
| 	pl->size = w;
 | |
| 	pl->adjusted = TRUE;
 | |
| 	i = (int)(pl - b->lines);
 | |
| 	DEBUG(Dprint_lines(b, b->first, b->last));
 | |
|       } else				/* put in next line */
 | |
|       { TextLine nl;
 | |
| 	int move = tl->size - w;
 | |
| 
 | |
| 	if ( i == b->last )
 | |
| 	  rlc_add_line(b);
 | |
| 	nl = &b->lines[NextLine(b, i)];
 | |
| 	nl->text = rlc_realloc(nl->text, (nl->size + move)*sizeof(text_char));
 | |
| 	memmove(&nl->text[move], nl->text, nl->size*sizeof(text_char));
 | |
| 	memmove(nl->text, &tl->text[w], move*sizeof(text_char));
 | |
| 	nl->size += move;
 | |
| 	tl->size = w;
 | |
|       }
 | |
|     } else if ( tl->text && tl->softreturn && tl->size < w )
 | |
|     { TextLine nl;
 | |
| 
 | |
|       if ( i == b->last )
 | |
| 	rlc_add_line(b);
 | |
|       nl = &b->lines[NextLine(b, i)];
 | |
| 
 | |
|       nl->text = rlc_realloc(nl->text, (nl->size + tl->size)*sizeof(text_char));
 | |
|       memmove(&nl->text[tl->size], nl->text, nl->size*sizeof(text_char));
 | |
|       memmove(nl->text, tl->text, tl->size*sizeof(text_char));
 | |
|       nl->size += tl->size;
 | |
|       nl->adjusted = TRUE;
 | |
|       rlc_shift_lines_up(b, i);
 | |
|     }
 | |
| 
 | |
|     if ( i == b->last )
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   for(i = NextLine(b, i); i != b->first; i = NextLine(b, i))
 | |
|     rlc_free_line(b, i);
 | |
| 
 | |
|   if ( rlc_count_lines(b, b->first, b->last) < h )
 | |
|     b->window_start = b->first;
 | |
|   else
 | |
|     b->window_start = rlc_add_lines(b, b->last, -(h-1));
 | |
| 
 | |
|   b->caret_y = b->last;
 | |
|   b->caret_x = b->lines[b->last].size;
 | |
| 
 | |
|   b->changed |= CHG_CARET|CHG_CHANGED|CHG_CLEAR;
 | |
| 
 | |
|   rlc_check_assertions(b);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_reinit_line(RlcData b, int line)
 | |
| { TextLine tl = &b->lines[line];
 | |
| 
 | |
|   tl->text	 = NULL;
 | |
|   tl->adjusted   = FALSE;
 | |
|   tl->size       = 0;
 | |
|   tl->softreturn = FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_free_line(RlcData b, int line)
 | |
| { TextLine tl = &b->lines[line];
 | |
|   if ( tl->text )
 | |
|   { rlc_free(tl->text);
 | |
|     rlc_reinit_line(b, line);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_adjust_line(RlcData b, int line)
 | |
| { TextLine tl = &b->lines[line];
 | |
| 
 | |
|   if ( tl->text && !tl->adjusted )
 | |
|   { tl->text = rlc_realloc(tl->text, tl->size == 0
 | |
| 				? sizeof(text_char)
 | |
| 				: tl->size * sizeof(text_char));
 | |
|     tl->adjusted = TRUE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_unadjust_line(RlcData b, int line)
 | |
| { TextLine tl = &b->lines[line];
 | |
| 
 | |
|   if ( tl->text )
 | |
|   { if ( tl->adjusted )
 | |
|     { tl->text = rlc_realloc(tl->text, (b->width + 1)*sizeof(text_char));
 | |
|       tl->adjusted = FALSE;
 | |
|     }
 | |
|   } else
 | |
|   { tl->text = rlc_malloc((b->width + 1)*sizeof(text_char));
 | |
|     tl->adjusted = FALSE;
 | |
|     tl->size = 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_open_line(RlcData b)
 | |
| { int i = b->last;
 | |
| 
 | |
|   if ( i == b->sel_start_line )
 | |
|     rlc_set_selection(b, 0, 0, 0, 0);	/* clear the selection */
 | |
|   if ( i == b->first )
 | |
|   { rlc_free_line(b, b->first);
 | |
|     b->first = NextLine(b, b->first);
 | |
|   }
 | |
| 
 | |
|   b->lines[i].text       = rlc_malloc((b->width + 1)*sizeof(text_char));
 | |
|   b->lines[i].adjusted   = FALSE;
 | |
|   b->lines[i].size       = 0;
 | |
|   b->lines[i].softreturn = FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_add_line(RlcData b)
 | |
| { b->last = NextLine(b, b->last);
 | |
|   rlc_open_line(b);
 | |
| }
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	   CALCULATIONS		*
 | |
| 		 *******************************/
 | |
| 
 | |
| static int
 | |
| rlc_count_lines(RlcData b, int from, int to)
 | |
| { if ( to >= from )
 | |
|     return to-from;
 | |
| 
 | |
|   return to + b->height - from;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| rlc_add_lines(RlcData b, int here, int add)
 | |
| { here += add;
 | |
|   while ( here < 0 )
 | |
|     here += b->height;
 | |
|   while ( here >= b->height )
 | |
|     here -= b->height;
 | |
| 
 | |
|   return here;
 | |
| }
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *    ANSI SEQUENCE HANDLING	*
 | |
| 		 *******************************/
 | |
| 
 | |
| static void
 | |
| rlc_need_arg(RlcData b, int arg, int def)
 | |
| { if ( b->argc < arg )
 | |
|   { b->argv[arg-1] = def;
 | |
|     b->argc = arg;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_caret_up(RlcData b, int arg)
 | |
| { while(arg-- > 0 && b->caret_y != b->first)
 | |
|     b->caret_y = PrevLine(b, b->caret_y);
 | |
| 
 | |
|   b->changed |= CHG_CARET;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_caret_down(RlcData b, int arg)
 | |
| { while ( arg-- > 0 )
 | |
|   { if ( b->caret_y == b->last )
 | |
|       rlc_add_line(b);
 | |
|     b->caret_y = NextLine(b, b->caret_y);
 | |
|     b->lines[b->caret_y].softreturn = FALSE; /* ? why not only on open? */
 | |
|   }
 | |
|   b->changed |= CHG_CARET;
 | |
| 					/* scroll? */
 | |
|   if ( rlc_count_lines(b, b->window_start, b->caret_y) >= b->window_size )
 | |
|   { b->window_start = rlc_add_lines(b, b->caret_y, -(b->window_size-1));
 | |
|     b->changed |= CHG_CHANGED|CHG_CLEAR;
 | |
|   }
 | |
| 
 | |
|   rlc_check_assertions(b);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_caret_forward(RlcData b, int arg)
 | |
| { while(arg-- > 0)
 | |
|   { if ( ++b->caret_x >= b->width )
 | |
|     { b->lines[b->caret_y].softreturn = TRUE;
 | |
|       b->caret_x = 0;
 | |
|       rlc_caret_down(b, 1);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   b->changed |= CHG_CARET;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_caret_backward(RlcData b, int arg)
 | |
| { while(arg-- > 0)
 | |
|   { if ( b->caret_x-- == 0 )
 | |
|     { rlc_caret_up(b, 1);
 | |
|       b->caret_x = b->width-1;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   b->changed |= CHG_CARET;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_cariage_return(RlcData b)
 | |
| { b->caret_x = 0;
 | |
| 
 | |
|   b->changed |= CHG_CARET;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_tab(RlcData b)
 | |
| { TextLine tl = &b->lines[b->caret_y];
 | |
| 
 | |
|   do
 | |
|   { rlc_caret_forward(b, 1);
 | |
|   } while( (b->caret_x % 8) != 0 );
 | |
| 
 | |
|   if ( tl->size < b->caret_x )
 | |
|   { rlc_unadjust_line(b, b->caret_y);
 | |
| 
 | |
|     while ( tl->size < b->caret_x )
 | |
|     { text_char *tc = &tl->text[tl->size++];
 | |
| 
 | |
|       tc->code = ' ';
 | |
|       tc->flags = b->sgr_flags;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   b->changed |= CHG_CARET;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_set_caret(RlcData b, int x, int y)
 | |
| { int cy = rlc_count_lines(b, b->window_start, b->caret_y);
 | |
| 
 | |
|   y = Bounds(y, 0, b->window_size);
 | |
| 
 | |
|   if ( y < cy )
 | |
|     b->caret_y = rlc_add_lines(b, b->window_start, y);
 | |
|   else
 | |
|     rlc_caret_down(b, y-cy);
 | |
| 
 | |
|   b->caret_x = Bounds(x, 0, b->width-1);
 | |
| 
 | |
|   b->changed |= CHG_CARET;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_save_caret_position(RlcData b)
 | |
| { b->scaret_y = rlc_count_lines(b, b->window_start, b->caret_y);
 | |
|   b->scaret_x = b->caret_x;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_restore_caret_position(RlcData b)
 | |
| { rlc_set_caret(b, b->scaret_x, b->scaret_y);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_erase_display(RlcData b)
 | |
| { int i = b->window_start;
 | |
|   int last = rlc_add_lines(b, b->window_start, b->window_size);
 | |
| 
 | |
|   do
 | |
|   { b->lines[i].size = 0;
 | |
|     i = NextLine(b, i);
 | |
|   } while ( i != last );
 | |
| 
 | |
|   b->changed |= CHG_CHANGED|CHG_CLEAR|CHG_CARET;
 | |
| 
 | |
|   rlc_set_caret(b, 0, 0);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_erase_line(RlcData b)
 | |
| { TextLine tl = &b->lines[b->caret_y];
 | |
| 
 | |
|   tl->size = b->caret_x;
 | |
|   tl->changed |= CHG_CHANGED|CHG_CLEAR;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_sgr(RlcData b, int sgr)
 | |
| { if ( sgr == 0 )
 | |
|   { b->sgr_flags = TF_DEFAULT;
 | |
|   } else if ( sgr >= 30 && sgr <= 39 )
 | |
|   { b->sgr_flags = TF_SET_FG(b->sgr_flags,
 | |
| 			     sgr == 39 ? ANSI_COLOR_DEFAULT : sgr-30);
 | |
|   } else if ( sgr >= 40 && sgr <= 49 )
 | |
|   { b->sgr_flags = TF_SET_BG(b->sgr_flags,
 | |
| 			     sgr == 49 ? ANSI_COLOR_DEFAULT : sgr-40);
 | |
|   } else if ( sgr >= 90 && sgr <= 99 )
 | |
|   { b->sgr_flags = TF_SET_FG(b->sgr_flags,
 | |
| 			     sgr == 99 ? ANSI_COLOR_DEFAULT : sgr-90+8);
 | |
|   } else if ( sgr >= 100 && sgr <= 109 )
 | |
|   { b->sgr_flags = TF_SET_BG(b->sgr_flags,
 | |
| 			     sgr == 109 ? ANSI_COLOR_DEFAULT : sgr-100+8);
 | |
|   } else if ( sgr == 1 )
 | |
|   { b->sgr_flags = TF_SET_BOLD(b->sgr_flags, 1);
 | |
|   } else if ( sgr == 4 )
 | |
|   { b->sgr_flags = TF_SET_UNDERLINE(b->sgr_flags, 1);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_put(RlcData b, int chr)
 | |
| { TextLine tl = &b->lines[b->caret_y];
 | |
|   text_char *tc;
 | |
| 
 | |
|   rlc_unadjust_line(b, b->caret_y);
 | |
|   while( tl->size < b->caret_x )
 | |
|   { tc = &tl->text[tl->size++];
 | |
| 
 | |
|     tc->code  = ' ';
 | |
|     tc->flags = b->sgr_flags;
 | |
|   }
 | |
|   tc = &tl->text[b->caret_x];
 | |
|   tc->code = chr;
 | |
|   tc->flags = b->sgr_flags;
 | |
|   if ( tl->size <= b->caret_x )
 | |
|     tl->size = b->caret_x + 1;
 | |
|   tl->changed |= CHG_CHANGED;
 | |
| 
 | |
|   rlc_caret_forward(b, 1);
 | |
| }
 | |
| 
 | |
| #ifdef _DEBUG
 | |
| #define CMD(c) {cmd = _T(#c); c;}
 | |
| #else
 | |
| #define CMD(c) {c;}
 | |
| #endif
 | |
| 
 | |
| static void
 | |
| rlc_putansi(RlcData b, int chr)
 | |
| {
 | |
| #ifdef _DEBUG
 | |
|   TCHAR *cmd;
 | |
| #endif
 | |
| 
 | |
|   switch(b->cmdstat)
 | |
|   { case CMD_INITIAL:
 | |
|       switch(chr)
 | |
|       { case '\b':
 | |
| 	  CMD(rlc_caret_backward(b, 1));
 | |
| 	  break;
 | |
|         case Control('G'):
 | |
| 	  MessageBeep(MB_ICONEXCLAMATION);
 | |
| 	  break;
 | |
| 	case '\r':
 | |
| 	  CMD(rlc_cariage_return(b));
 | |
| 	  break;
 | |
| 	case '\n':
 | |
| 	  CMD(rlc_caret_down(b, 1));
 | |
| 	  break;
 | |
| 	case '\t':
 | |
| 	  CMD(rlc_tab(b));
 | |
| 	  break;
 | |
| 	case 27:			/* ESC */
 | |
| 	  b->cmdstat = CMD_ESC;
 | |
| 	  break;
 | |
| 	default:
 | |
| 	  CMD(rlc_put(b, chr));
 | |
| 	  break;
 | |
|       }
 | |
|       break;
 | |
|     case CMD_ESC:
 | |
|       switch(chr)
 | |
|       { case '[':
 | |
| 	  b->cmdstat = CMD_ANSI;
 | |
| 	  b->argc    = 0;
 | |
| 	  b->argstat = 0;		/* no arg */
 | |
| 	  break;
 | |
| 	default:
 | |
| 	  b->cmdstat = CMD_INITIAL;
 | |
| 	  break;
 | |
|       }
 | |
|       break;
 | |
|     case CMD_ANSI:			/* ESC [ */
 | |
|       if ( _istdigit((wint_t)chr) )
 | |
|       { if ( !b->argstat )
 | |
| 	{ b->argv[b->argc] = (chr - '0');
 | |
| 	  b->argstat = 1;		/* positive */
 | |
| 	} else
 | |
| 	{ b->argv[b->argc] = b->argv[b->argc] * 10 + (chr - '0');
 | |
| 	}
 | |
| 
 | |
| 	break;
 | |
|       }
 | |
|       if ( !b->argstat && chr == '-' )
 | |
|       { b->argstat = -1;		/* negative */
 | |
| 	break;
 | |
|       }
 | |
|       if ( b->argstat )
 | |
|       { b->argv[b->argc] *= b->argstat;
 | |
| 	if ( b->argc < (ANSI_MAX_ARGC-1) )
 | |
| 	  b->argc++;			/* silently discard more of them */
 | |
| 	b->argstat = 0;
 | |
|       }
 | |
|       switch(chr)
 | |
|       { case ';':
 | |
| 	  return;			/* wait for more args */
 | |
| 	case 'H':
 | |
| 	case 'f':
 | |
| 	  rlc_need_arg(b, 1, 0);
 | |
| 	  rlc_need_arg(b, 2, 0);
 | |
| 	  CMD(rlc_set_caret(b, b->argv[0], b->argv[1]));
 | |
| 	  break;
 | |
| 	case 'A':
 | |
| 	  rlc_need_arg(b, 1, 1);
 | |
| 	  CMD(rlc_caret_up(b, b->argv[0]));
 | |
| 	  break;
 | |
| 	case 'B':
 | |
| 	  rlc_need_arg(b, 1, 1);
 | |
| 	  CMD(rlc_caret_down(b, b->argv[0]));
 | |
| 	  break;
 | |
| 	case 'C':
 | |
| 	  rlc_need_arg(b, 1, 1);
 | |
| 	  CMD(rlc_caret_forward(b, b->argv[0]));
 | |
| 	  break;
 | |
| 	case 'D':
 | |
| 	  rlc_need_arg(b, 1, 1);
 | |
| 	  CMD(rlc_caret_backward(b, b->argv[0]));
 | |
| 	  break;
 | |
| 	case 's':
 | |
| 	  CMD(rlc_save_caret_position(b));
 | |
| 	  break;
 | |
| 	case 'u':
 | |
| 	  CMD(rlc_restore_caret_position(b));
 | |
| 	  break;
 | |
| 	case 'J':
 | |
| 	  if ( b->argv[0] == 2 )
 | |
| 	    CMD(rlc_erase_display(b));
 | |
| 	  break;
 | |
| 	case 'K':
 | |
| 	  CMD(rlc_erase_line(b));
 | |
| 	  break;
 | |
| 	case 'm':
 | |
| 	  { int i;
 | |
| 	    rlc_need_arg(b, 1, 0);
 | |
| 
 | |
| 	    for(i=0; i<b->argc; i++)
 | |
| 	      CMD(rlc_sgr(b, b->argv[i]));
 | |
| 	    break;
 | |
| 	  }
 | |
|       }
 | |
|       b->cmdstat = CMD_INITIAL;
 | |
|   }
 | |
| 
 | |
|   rlc_check_assertions(b);
 | |
| }
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	      CUT/PASTE		*
 | |
| 		 *******************************/
 | |
| 
 | |
| static void
 | |
| rlc_paste(RlcData b)
 | |
| { HGLOBAL mem;
 | |
| 
 | |
|   if ( b->window )
 | |
|   { OpenClipboard(b->window);
 | |
|     if ( (mem = GetClipboardData(CF_UNICODETEXT)) )
 | |
|     { wchar_t *data = GlobalLock(mem);
 | |
|       int i;
 | |
|       RlcQueue q = b->queue;
 | |
| 
 | |
|       if ( q )
 | |
|       { for(i=0; data[i]; i++)
 | |
| 	{ rlc_add_queue(b, q, data[i]);
 | |
| 	  if ( data[i] == '\r' && data[i+1] == '\n' )
 | |
| 	    i++;
 | |
| 	}
 | |
|       }
 | |
| 
 | |
|       GlobalUnlock(mem);
 | |
|     } else if ( (mem = GetClipboardData(CF_TEXT)) )
 | |
|     { char far *data = GlobalLock(mem);
 | |
|       int i;
 | |
|       RlcQueue q = b->queue;
 | |
| 
 | |
|       if ( q )
 | |
|       { for(i=0; data[i]; i++)
 | |
| 	{ rlc_add_queue(b, q, data[i]);
 | |
| 	  if ( data[i] == '\r' && data[i+1] == '\n' )
 | |
| 	    i++;
 | |
| 	}
 | |
|       }
 | |
| 
 | |
|       GlobalUnlock(mem);
 | |
|     }
 | |
|     CloseClipboard();
 | |
|   }
 | |
| }
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	LINE-READ SUPPORT	*
 | |
| 		 *******************************/
 | |
| 
 | |
| void
 | |
| rlc_get_mark(rlc_console c, RlcMark m)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   m->mark_x = b->caret_x;
 | |
|   m->mark_y = b->caret_y;
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| rlc_goto_mark(rlc_console c, RlcMark m, const TCHAR *data, size_t offset)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   b->caret_x = m->mark_x;
 | |
|   b->caret_y = m->mark_y;
 | |
| 
 | |
|   for( ; offset-- > 0; data++ )
 | |
|   { switch(*data)
 | |
|     { case '\t':
 | |
| 	rlc_tab(b);
 | |
| 	break;
 | |
|       case '\n':
 | |
| 	b->caret_x = 0;
 | |
|         rlc_caret_down(b, 1);
 | |
| 	break;
 | |
|       default:
 | |
| 	rlc_caret_forward(b, 1);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| rlc_erase_from_caret(rlc_console c)
 | |
| { RlcData b = rlc_get_data(c);
 | |
|   int i = b->caret_y;
 | |
|   int x = b->caret_x;
 | |
|   int last = rlc_add_lines(b, b->window_start, b->window_size);
 | |
| 
 | |
|   do
 | |
|   { TextLine tl = &b->lines[i];
 | |
| 
 | |
|     if ( tl->size != x )
 | |
|     { tl->size = x;
 | |
|       tl->changed |= CHG_CHANGED|CHG_CLEAR;
 | |
|     }
 | |
| 
 | |
|     i = NextLine(b, i);
 | |
|     x = 0;
 | |
|   } while ( i != last );
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| rlc_putchar(rlc_console c, int chr)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   rlc_putansi(b, chr);
 | |
| }
 | |
| 
 | |
| 
 | |
| TCHAR *
 | |
| rlc_read_screen(rlc_console c, RlcMark f, RlcMark t)
 | |
| { RlcData b = rlc_get_data(c);
 | |
|   TCHAR *buf;
 | |
| 
 | |
|   buf = rlc_read_from_window(b, f->mark_y, f->mark_x, t->mark_y, t->mark_x);
 | |
| 
 | |
|   return buf;
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| rlc_update(rlc_console c)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   if ( b->window )
 | |
|   { rlc_normalise(b);
 | |
|     rlc_request_redraw(b);
 | |
|     UpdateWindow(b->window);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	  UPDATE THREAD		*
 | |
| 		 *******************************/
 | |
| 
 | |
| DWORD WINAPI
 | |
| window_loop(LPVOID arg)
 | |
| { RlcData b = (RlcData) arg;
 | |
| 
 | |
|   rlc_create_window(b);
 | |
| 					/* if we do not do this, all windows */
 | |
| 					/* created by Prolog (XPCE) will be */
 | |
| 					/* in the background and inactive! */
 | |
|   if ( !AttachThreadInput(b->application_thread_id,
 | |
| 			  b->console_thread_id, TRUE) )
 | |
|     rlc_putansi(b, '!');
 | |
| 
 | |
|   PostThreadMessage(b->application_thread_id, WM_RLC_READY, 0, 0);
 | |
| 
 | |
|   while(!b->closing)
 | |
|   { switch( b->imode )
 | |
|     { case IMODE_COOKED:
 | |
|       { TCHAR *line = read_line(b);
 | |
| 
 | |
| 	if ( line != RL_CANCELED_CHARP )
 | |
| 	{ LQueued lq = rlc_malloc(sizeof(lqueued));
 | |
| 
 | |
| 	  lq->next = NULL;
 | |
| 	  lq->line = line;
 | |
| 
 | |
| 	  if ( b->ltail )
 | |
| 	  { b->ltail->next = lq;
 | |
| 	    b->ltail = lq;
 | |
| 	  } else
 | |
| 	  { b->lhead = b->ltail = lq;
 | |
| 					      /* awake main thread */
 | |
| 	    PostThreadMessage(b->application_thread_id, WM_RLC_INPUT, 0, 0);
 | |
| 	  }
 | |
| 	}
 | |
| 
 | |
| 	break;
 | |
|       }
 | |
|       case IMODE_RAW:
 | |
|       { MSG msg;
 | |
| 
 | |
| 	if ( rlc_get_message(&msg, NULL, 0, 0) )
 | |
| 	{ TranslateMessage(&msg);
 | |
| 	  DispatchMessage(&msg);
 | |
| 	  rlc_flush_output(b);
 | |
| 	} else
 | |
| 	  goto out;
 | |
| 
 | |
| 	if ( b->imodeswitch )
 | |
| 	{ b->imodeswitch = FALSE;
 | |
| 	}
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if ( b->closing <= 2 )
 | |
|   { MSG msg;
 | |
|     TCHAR *waiting = _T("\r\nWaiting for Prolog. ")
 | |
| 		     _T("Close again to force termination ..");
 | |
| 
 | |
|     rlc_write(b, waiting, _tcslen(waiting));
 | |
| 
 | |
|     while ( b->closing <= 2 && rlc_get_message(&msg, NULL, 0, 0) )
 | |
|     { TranslateMessage(&msg);
 | |
|       DispatchMessage(&msg);
 | |
|       rlc_flush_output(b);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| out:
 | |
| { DWORD appthread = b->application_thread_id;
 | |
|   rlc_destroy(b);
 | |
| 
 | |
|   PostThreadMessage(appthread, WM_RLC_READY, 0, 0);
 | |
| }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	  WATCOM/DOS I/O	*
 | |
| 		 *******************************/
 | |
| 
 | |
| int
 | |
| getch(rlc_console c)
 | |
| { RlcData b = rlc_get_data(c);
 | |
|   RlcQueue q = b->queue;
 | |
|   int fromcon = (GetCurrentThreadId() == b->console_thread_id);
 | |
| 
 | |
|   while( rlc_is_empty_queue(q) )
 | |
|   { if ( q->flags & RLC_EOF )
 | |
|       return EOF;
 | |
| 
 | |
|     if ( !fromcon )
 | |
|     { MSG msg;
 | |
| 
 | |
|       if ( rlc_get_message(&msg, NULL, 0, 0) )
 | |
|       { TranslateMessage(&msg);
 | |
| 	DispatchMessage(&msg);
 | |
|       } else
 | |
| 	return EOF;
 | |
|     } else
 | |
|     { rlc_dispatch(b);
 | |
|       if ( b->imodeswitch )
 | |
|       { b->imodeswitch = FALSE;
 | |
| 	return IMODE_SWITCH_CHAR;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return rlc_from_queue(q);
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| getche(rlc_console c)
 | |
| { RlcData b = rlc_get_data(c);
 | |
|   int chr = getch(b);
 | |
| 
 | |
|   rlc_putansi(b, chr);
 | |
|   return chr;
 | |
| }
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *        GO32 FUNCTIONS	*
 | |
| 		 *******************************/
 | |
| 
 | |
| int
 | |
| getkey(rlc_console con)
 | |
| { int c;
 | |
|   RlcData b = rlc_get_data(con);
 | |
|   int fromcon = (GetCurrentThreadId() == b->console_thread_id);
 | |
| 
 | |
|   if ( !fromcon && b->imode != IMODE_RAW )
 | |
|   { int old = b->imode;
 | |
| 
 | |
|     b->imode = IMODE_RAW;
 | |
|     b->imodeswitch = TRUE;
 | |
|     c = getch(b);
 | |
|     b->imode = old;
 | |
|     b->imodeswitch = TRUE;
 | |
|   } else
 | |
|     c = getch(b);
 | |
| 
 | |
|   return c;
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| kbhit(rlc_console c)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   return !rlc_is_empty_queue(b->queue);
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| ScreenGetCursor(rlc_console c, int *row, int *col)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   *row = rlc_count_lines(b, b->window_start, b->caret_y) + 1;
 | |
|   *col = b->caret_x + 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| ScreenSetCursor(rlc_console c, int row, int col)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   rlc_set_caret(b, col-1, row-1);
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| ScreenCols(rlc_console c)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   return b->width;
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| ScreenRows(rlc_console c)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   return b->window_size;
 | |
| }
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	      QUEUE		*
 | |
| 		 *******************************/
 | |
| 
 | |
| #define QN(q, i) ((i)+1 >= (q)->size ? 0 : (i)+1)
 | |
| 
 | |
| 
 | |
| RlcQueue
 | |
| rlc_make_queue(int size)
 | |
| { RlcQueue q;
 | |
| 
 | |
|   if ( (q = rlc_malloc(sizeof(rlc_queue))) )
 | |
|   { q->first = q->last = 0;
 | |
|     q->size = size;
 | |
|     q->flags = 0;
 | |
| 
 | |
|     if ( (q->buffer = rlc_malloc(sizeof(TCHAR) * size)) )
 | |
|       return q;
 | |
|   }
 | |
| 
 | |
|   return NULL;				/* not enough memory */
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| rlc_resize_queue(RlcQueue q, int size)
 | |
| { TCHAR *newbuf;
 | |
| 
 | |
|   if ( (newbuf = rlc_malloc(size*sizeof(TCHAR))) )
 | |
|   { TCHAR *o = newbuf;
 | |
|     int c;
 | |
| 
 | |
|     while( (c=rlc_from_queue(q)) != -1 )
 | |
|       *o++ = c;
 | |
| 
 | |
|     if ( q->buffer )
 | |
|       rlc_free(q->buffer);
 | |
|     q->buffer = newbuf;
 | |
|     q->first = 0;
 | |
|     q->last  = (int)(o-newbuf);
 | |
|     q->size  = size;
 | |
| 
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| rlc_add_queue(RlcData b, RlcQueue q, int chr)
 | |
| { int empty = (q->first == q->last);
 | |
| 
 | |
|   while(q->size < 50000)
 | |
|   { if ( QN(q, q->last) != q->first )
 | |
|     { q->buffer[q->last] = chr;
 | |
|       q->last = QN(q, q->last);
 | |
| 
 | |
|       if ( empty )
 | |
| 	PostThreadMessage(b->application_thread_id, WM_RLC_INPUT, 0, 0);
 | |
| 
 | |
|       return TRUE;
 | |
|     }
 | |
| 
 | |
|     rlc_resize_queue(q, q->size*2);
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| rlc_is_empty_queue(RlcQueue q)
 | |
| { if ( q->first == q->last )
 | |
|     return TRUE;
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| rlc_from_queue(RlcQueue q)
 | |
| { if ( q->first != q->last )
 | |
|   { int chr = q->buffer[q->first];
 | |
| 
 | |
|     q->first = QN(q, q->first);
 | |
| 
 | |
|     return chr;
 | |
|   }
 | |
| 
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	   BUFFERED I/O		*
 | |
| 		 *******************************/
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| When using UNICODE, count is in bytes!
 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| size_t
 | |
| rlc_read(rlc_console c, TCHAR *buf, size_t count)
 | |
| { RlcData d = rlc_get_data(c);
 | |
|   size_t give;
 | |
|   MSG msg;
 | |
| 
 | |
|   if ( d->closing )
 | |
|     return 0;				/* signal EOF when closing */
 | |
| 
 | |
|   PostThreadMessage(d->console_thread_id,
 | |
| 		    WM_RLC_FLUSH,
 | |
| 		    0, 0);
 | |
|   if ( _rlc_update_hook )
 | |
|     (*_rlc_update_hook)();
 | |
| 
 | |
|   d->promptbuf[d->promptlen] = EOS;
 | |
|   _tcscpy(d->prompt, d->promptbuf);
 | |
| 
 | |
|   if ( d->read_buffer.given >= d->read_buffer.length )
 | |
|   { if ( d->read_buffer.line )
 | |
|     { rlc_free(d->read_buffer.line);
 | |
|       d->read_buffer.line = NULL;
 | |
|     }
 | |
| 
 | |
|     if ( d->imode != IMODE_COOKED )
 | |
|     { d->imode = IMODE_COOKED;
 | |
|       d->imodeswitch = TRUE;
 | |
|     }
 | |
| 
 | |
|     while(!d->lhead)
 | |
|     { if ( rlc_get_message(&msg, NULL, 0, 0) )
 | |
|       { TranslateMessage(&msg);
 | |
| 	DispatchMessage(&msg);
 | |
|       } else
 | |
| 	return -1;
 | |
|     }
 | |
| 
 | |
|     { LQueued lq = d->lhead;
 | |
|       d->read_buffer.line = lq->line;
 | |
|       if ( lq->next )
 | |
| 	d->lhead = lq->next;
 | |
|       else
 | |
| 	d->lhead = d->ltail = NULL;
 | |
| 
 | |
|       rlc_free(lq);
 | |
|     }
 | |
| 
 | |
|     d->read_buffer.length = _tcslen(d->read_buffer.line);
 | |
|     d->read_buffer.given = 0;
 | |
|   }
 | |
| 
 | |
|   if ( d->read_buffer.length - d->read_buffer.given > count )
 | |
|     give = count;
 | |
|   else
 | |
|     give = d->read_buffer.length - d->read_buffer.given;
 | |
| 
 | |
|   _tcsncpy(buf, d->read_buffer.line+d->read_buffer.given, give);
 | |
|   d->read_buffer.given += give;
 | |
| 
 | |
|   return give;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| rlc_do_write(RlcData b, TCHAR *buf, int count)
 | |
| { if ( count > 0 )
 | |
|   { int n = 0;
 | |
|     TCHAR *s = buf;
 | |
| 
 | |
|     while(n++ < count)
 | |
|     { int chr = *s++;
 | |
| 
 | |
|       if ( chr == '\n' )
 | |
| 	rlc_putansi(b, '\r');
 | |
|       rlc_putansi(b, chr);
 | |
|     }
 | |
| 
 | |
|     rlc_normalise(b);
 | |
|     if ( b->window )
 | |
|     { rlc_request_redraw(b);
 | |
|       UpdateWindow(b->window);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| rlc_flush_output(rlc_console c)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   if ( !b )
 | |
|     return -1;
 | |
| 
 | |
|   if ( b->output_queued )
 | |
|   { rlc_do_write(b, b->output_queue, b->output_queued);
 | |
| 
 | |
|     b->output_queued = 0;
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| size_t
 | |
| rlc_write(rlc_console c, TCHAR *buf, size_t count)
 | |
| { DWORD_PTR result;
 | |
|   TCHAR *e, *s;
 | |
|   RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   if ( !b )
 | |
|     return -1;
 | |
| 
 | |
|   for(s=buf, e=&buf[count]; s<e; s++)
 | |
|   { if ( *s == '\n' )
 | |
|       b->promptlen = 0;
 | |
|     else if ( b->promptlen < MAXPROMPT-1 )
 | |
|       b->promptbuf[b->promptlen++] = *s;
 | |
|   }
 | |
| 
 | |
|   if ( b->window )
 | |
|   { if ( SendMessageTimeout(b->window,
 | |
| 			    WM_RLC_WRITE,
 | |
| 			    (WPARAM)count,
 | |
| 			    (LPARAM)buf,
 | |
| 			    SMTO_NORMAL,
 | |
| 			    10000,
 | |
| 			    &result) )
 | |
|     { PostMessage(b->window,
 | |
| 		  WM_RLC_FLUSH,
 | |
| 		  0, 0);
 | |
|       return count;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return -1;				/* I/O error */
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| free_rlc_data(RlcData b)
 | |
| { b->magic = 42;			/* so next gets errors */
 | |
| 
 | |
|   if ( b->lines )
 | |
|   { int i;
 | |
| 
 | |
|     for(i=0; i<b->height; i++)
 | |
|     { if ( b->lines[i].text )
 | |
| 	free(b->lines[i].text);
 | |
|     }
 | |
| 
 | |
|     free(b->lines);
 | |
|   }
 | |
|   if ( b->read_buffer.line )
 | |
|     free(b->read_buffer.line);
 | |
| 
 | |
|   free(b);
 | |
| }
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| rlc_close() tries to gracefully get rid of   the console thread. It does
 | |
| so by posting WM_RLC_CLOSEWIN and then waiting for a WM_RLC_READY reply.
 | |
| It waits for a maximum of  1.5  second,   which  should  be  fine as the
 | |
| console thread should not have intptr_t-lasting activities.
 | |
| 
 | |
| If the timeout expires it hopes for the best. This was the old situation
 | |
| and proved to be sound on Windows-NT, but not on 95 and '98.
 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| int
 | |
| rlc_close(rlc_console c)
 | |
| { RlcData b = (RlcData)c;
 | |
|   MSG msg;
 | |
|   int i;
 | |
| 
 | |
|   if ( b->magic != RLC_MAGIC )
 | |
|     return -1;
 | |
| 
 | |
|   rlc_save_options(b);
 | |
|   b->closing = 3;
 | |
|   PostMessage(b->window, WM_RLC_CLOSEWIN, 0, 0);
 | |
| 
 | |
| 					/* wait for termination */
 | |
|   for(i=0; i<30; i++)
 | |
|   { if ( PeekMessage(&msg, NULL, WM_RLC_READY, WM_RLC_READY, PM_REMOVE) )
 | |
|       break;
 | |
|     Sleep(50);
 | |
|   }
 | |
| 
 | |
|   b->magic = 0;
 | |
|   free_user_data(c);
 | |
|   free_rlc_data(b);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| const TCHAR *
 | |
| rlc_prompt(rlc_console c, const TCHAR *new)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   if ( b )
 | |
|   { if ( new )
 | |
|     { _tcsncpy(b->prompt, new, MAXPROMPT);
 | |
|       b->prompt[MAXPROMPT-1] = EOS;
 | |
|     }
 | |
| 
 | |
|     return b->prompt;
 | |
|   }
 | |
| 
 | |
|   return _T("");
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| rlc_clearprompt(rlc_console c)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   if ( b )
 | |
|   { b->promptlen = 0;
 | |
|     b->prompt[0] = EOS;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	    MISC STUFF		*
 | |
| 		 *******************************/
 | |
| 
 | |
| void
 | |
| rlc_title(rlc_console c, TCHAR *title, TCHAR *old, int size)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   if ( old )
 | |
|     memmove(old, b->current_title, size*sizeof(TCHAR));
 | |
| 
 | |
|   if ( title )
 | |
|   { if ( b->window )
 | |
|       SetWindowText(b->window, title);
 | |
| 
 | |
|     memmove(b->current_title, title, RLC_TITLE_MAX*sizeof(TCHAR));
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| rlc_icon(rlc_console c, HICON icon)
 | |
| {
 | |
|   SetClassLongPtr(rlc_hwnd(c), GCLP_HICON, (LONG_PTR) icon);
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| rlc_window_pos(rlc_console c,
 | |
| 	       HWND hWndInsertAfter,
 | |
| 	       int x, int y, int w, int h,
 | |
| 	       UINT flags)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   if ( b )
 | |
|   { w *= b->cw;
 | |
|     h *= b->ch;
 | |
| 
 | |
|     SetWindowPos(b->window, hWndInsertAfter,
 | |
| 		 x, y, w, h,
 | |
| 		 flags);
 | |
| 
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| HANDLE
 | |
| rlc_hinstance()
 | |
| { return _rlc_hinstance;
 | |
| }
 | |
| 
 | |
| 
 | |
| HWND
 | |
| rlc_hwnd(rlc_console c)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   return b ? b->window : (HWND)NULL;
 | |
| }
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	 SETTING OPTIONS	*
 | |
| 		 *******************************/
 | |
| 
 | |
| int
 | |
| rlc_copy_output_to_debug_output(int new)
 | |
| { int old = _rlc_copy_output_to_debug_output;
 | |
| 
 | |
|   _rlc_copy_output_to_debug_output = new;
 | |
| 
 | |
|   return old;
 | |
| }
 | |
| 
 | |
| RlcUpdateHook
 | |
| rlc_update_hook(RlcUpdateHook new)
 | |
| { RlcUpdateHook old = _rlc_update_hook;
 | |
| 
 | |
|   _rlc_update_hook = new;
 | |
|   return old;
 | |
| }
 | |
| 
 | |
| RlcTimerHook
 | |
| rlc_timer_hook(RlcTimerHook new)
 | |
| { RlcTimerHook old = _rlc_timer_hook;
 | |
| 
 | |
|   _rlc_timer_hook = new;
 | |
|   return old;
 | |
| }
 | |
| 
 | |
| RlcRenderHook
 | |
| rlc_render_hook(RlcRenderHook new)
 | |
| { RlcRenderHook old = _rlc_render_hook;
 | |
| 
 | |
|   _rlc_render_hook = new;
 | |
|   return old;
 | |
| }
 | |
| 
 | |
| RlcRenderAllHook
 | |
| rlc_render_all_hook(RlcRenderAllHook new)
 | |
| { RlcRenderAllHook old = _rlc_render_all_hook;
 | |
| 
 | |
|   _rlc_render_all_hook = new;
 | |
|   return old;
 | |
| }
 | |
| 
 | |
| RlcInterruptHook
 | |
| rlc_interrupt_hook(RlcInterruptHook new)
 | |
| { RlcInterruptHook old = _rlc_interrupt_hook;
 | |
| 
 | |
|   _rlc_interrupt_hook = new;
 | |
|   return old;
 | |
| }
 | |
| 
 | |
| RlcResizeHook
 | |
| rlc_resize_hook(RlcResizeHook new)
 | |
| { RlcResizeHook old = _rlc_resize_hook;
 | |
| 
 | |
|   _rlc_resize_hook = new;
 | |
|   return old;
 | |
| }
 | |
| 
 | |
| RlcMenuHook
 | |
| rlc_menu_hook(RlcMenuHook new)
 | |
| { RlcMenuHook old = _rlc_menu_hook;
 | |
| 
 | |
|   _rlc_menu_hook = new;
 | |
|   return old;
 | |
| }
 | |
| 
 | |
| 
 | |
| RlcMessageHook
 | |
| rlc_message_hook(RlcMessageHook new)
 | |
| { RlcMessageHook old = _rlc_message_hook;
 | |
| 
 | |
|   _rlc_message_hook = new;
 | |
|   return old;
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| rlc_set(rlc_console c, int what, uintptr_t data, RlcFreeDataHook hook)
 | |
| { RlcData b = rlc_get_data(c);
 | |
| 
 | |
|   switch(what)
 | |
|   { default:
 | |
|       if ( what >= RLC_VALUE(0) &&
 | |
| 	   what <= RLC_VALUE(MAX_USER_VALUES) )
 | |
|       { b->values[what-RLC_VALUE(0)].data = data;
 | |
| 	b->values[what-RLC_VALUE(0)].hook = hook;
 | |
|         return TRUE;
 | |
|       }
 | |
|       return FALSE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| rlc_get(rlc_console c, int what, uintptr_t *data)
 | |
| { RlcData b = (RlcData)c;
 | |
| 
 | |
|   if ( !b )
 | |
|     return FALSE;
 | |
| 
 | |
|   switch(what)
 | |
|   { case RLC_APPLICATION_THREAD:
 | |
|       *data = (uintptr_t)b->application_thread;
 | |
|       return TRUE;
 | |
|     case RLC_APPLICATION_THREAD_ID:
 | |
|       *data = (uintptr_t)b->application_thread_id;
 | |
|       return TRUE;
 | |
|     default:
 | |
|       if ( what >= RLC_VALUE(0) &&
 | |
| 	   what <= RLC_VALUE(MAX_USER_VALUES) )
 | |
|       { *data = b->values[what-RLC_VALUE(0)].data;
 | |
|         return TRUE;
 | |
|       }
 | |
|       return FALSE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| free_user_data(RlcData b)
 | |
| { user_data *d = b->values;
 | |
|   int i;
 | |
| 
 | |
|   for(i=0; i<MAX_USER_VALUES; i++, d++)
 | |
|   { RlcFreeDataHook hook;
 | |
| 
 | |
|     if ( (hook=d->hook) )
 | |
|     { uintptr_t data = d->data;
 | |
|       d->hook = NULL;
 | |
|       d->data = 0L;
 | |
|       (*hook)(data);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	       UTIL		*
 | |
| 		 *******************************/
 | |
| 
 | |
| static void
 | |
| noMemory(void)
 | |
| { MessageBox(NULL, _T("Not enough memory"), _T("Console"), MB_OK|MB_TASKMODAL);
 | |
| 
 | |
|   ExitProcess(1);
 | |
| }
 | |
| 
 | |
| 
 | |
| void *
 | |
| rlc_malloc(size_t size)
 | |
| { void *ptr = malloc(size);
 | |
| 
 | |
|   if ( !ptr && size > 0 )
 | |
|     noMemory();
 | |
| 
 | |
| #ifdef _DEBUG
 | |
|   memset(ptr, 0xbf, size);
 | |
| #endif
 | |
|   return ptr;
 | |
| }
 | |
| 
 | |
| 
 | |
| void *
 | |
| rlc_realloc(void *ptr, size_t size)
 | |
| { void *ptr2 = realloc(ptr, size);
 | |
| 
 | |
|   if ( !ptr2 && size > 0 )
 | |
|     noMemory();
 | |
| 
 | |
|   return ptr2;
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| rlc_free(void *ptr)
 | |
| { free(ptr);
 | |
| }
 | |
| 
 | |
| #ifndef initHeapDebug
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	       DEBUG		*
 | |
| 		 *******************************/
 | |
| 
 | |
| static void
 | |
| initHeapDebug(void)
 | |
| { int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
 | |
| 
 | |
|   if ( !(tmpFlag & _CRTDBG_CHECK_ALWAYS_DF) )
 | |
|   { /*MessageBox(NULL,
 | |
| 	       "setting malloc() debugging",
 | |
| 	       "SWI-Prolog console",
 | |
| 	       MB_OK|MB_TASKMODAL);*/
 | |
|     tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF;
 | |
|     _CrtSetDbgFlag(tmpFlag);
 | |
|   } else
 | |
|   {
 | |
|     /*MessageBox(NULL,
 | |
| 	       "malloc() debugging lready set",
 | |
| 	       "SWI-Prolog console",
 | |
| 	       MB_OK|MB_TASKMODAL);*/
 | |
|   }
 | |
| }
 | |
| 
 | |
| #endif /*initHeapDebug*/
 | |
| 
 | |
| #ifdef _DEBUG
 | |
| 
 | |
| static void
 | |
| Dprintf(const TCHAR *fmt, ...)
 | |
| { TCHAR buf[1024];
 | |
|   va_list args;
 | |
| 
 | |
|   va_start(args, fmt);
 | |
|   vswprintf(buf, sizeof(buf)/sizeof(TCHAR), fmt, args);
 | |
|   va_end(args);
 | |
| 
 | |
|   OutputDebugString(buf);
 | |
| }
 | |
| 
 | |
| static void
 | |
| Dprint_lines(RlcData b, int from, int to)
 | |
| { TCHAR buf[1024];
 | |
| 
 | |
|   for( ; ; from = NextLine(b, from))
 | |
|   { TextLine tl = &b->lines[from];
 | |
| 
 | |
|     memcpy(buf, tl->text, tl->size);
 | |
|     buf[tl->size] = EOS;
 | |
|     Dprintf(_T("%03d: (0x%08x) \"%s\"\n"), from, tl->text, buf);
 | |
| 
 | |
|     if ( from == to )
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| #endif
 |