• R/O
  • HTTP
  • SSH
  • HTTPS

コミット

タグ
未設定

よく使われているワード(クリックで追加)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

The MinGW.org Installation Manager Tool


コミットメタ情報

リビジョン2fe2646c69cf31bcd03721ac35f7f56816712fdf (tree)
日時2012-12-05 22:50:11
作者Keith Marshall <keithmarshall@user...>
コミッターKeith Marshall

ログメッセージ

Implement PTY display emulation for GUI mode DMH.

変更サマリ

差分

--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
1+2012-12-05 Keith Marshall <keithmarshall@users.sourceforge.net>
2+
3+ Implement PTY display emulation for GUI mode DMH.
4+
5+ * src/dmhcore.h (dmhTypeGeneric::set_console_hook): New public virtual
6+ method; declare it, providing inline default do-nothing implementation.
7+ (dmhTypeGeneric::severity_tag): New protected static method; declare it.
8+ (dmhTypeGeneric::notification_format): New protected static property;
9+ declare it.
10+
11+ * src/dmh.cpp (dmhTypeGeneric::severity_tag): Implement it.
12+ (dmhTypeGeneric::notification_format): Define and initialise it.
13+ (dmhTypeTTY::notify): Use them.
14+ (dmh_setpty): New function; declare prototype and implement it.
15+
16+ * src/guidmh.cpp (dmhTypePTY): New locally implemented class.
17+ (dmhTypeGUI::set_console_hook): New virtual method; declare and
18+ implement it; it stores a dmhTypePTY reference pointer into...
19+ (dmhTypeGUI::console_hook): ...this new private member variable.
20+ (dmhTypeGUI::dmhTypeGUI): Initialise it.
21+ (dmhTypeGUI::notify) [console_hook != NULL]: Delegate to it.
22+ (dmhTypeGUI::printf) [console_hook != NULL]: Likewise.
23+
124 2012-12-03 Keith Marshall <keithmarshall@users.sourceforge.net>
225
326 Implement generalised API for dialogue boxes with worker threads.
--- a/src/dmh.cpp
+++ b/src/dmh.cpp
@@ -34,6 +34,12 @@
3434 #define WIN32_LEAN_AND_MEAN
3535 #include <windows.h>
3636
37+/* Prototype this at point of use, rather than in dmhcore.h,
38+ * so that we don't need to arbitrarily include windows.h in
39+ * any DMH client.
40+ */
41+EXTERN_C void dmh_setpty( HWND );
42+
3743 /* Implementation of the dmh_exception class.
3844 */
3945 static const char *unspecified_error = "Unspecified error";
@@ -161,9 +167,9 @@ inline int dmhTypeTTY::emit_and_flush( int status )
161167 return status;
162168 }
163169
164-int dmhTypeTTY::notify( const dmh_severity code, const char *fmt, va_list argv )
170+const char *dmhTypeGeneric::severity_tag( dmh_severity code )
165171 {
166- /* Message dispatcher for console class applications.
172+ /* Helper function to assign labels to the known severity codes.
167173 */
168174 static const char *severity[] =
169175 {
@@ -174,13 +180,24 @@ int dmhTypeTTY::notify( const dmh_severity code, const char *fmt, va_list argv )
174180 "ERROR", /* DMH_ERROR */
175181 "FATAL" /* DMH_FATAL */
176182 };
183+ return severity[code];
184+}
185+
186+/* Establish the format to be used for the prefix string, which is
187+ * attached to TTY and PTY notifications.
188+ */
189+const char *dmhTypeGeneric::notification_format = "%s: *** %s *** ";
177190
191+int dmhTypeTTY::notify( const dmh_severity code, const char *fmt, va_list argv )
192+{
193+ /* Message dispatcher for console class applications.
194+ */
178195 /* Dispatch the message to standard error, terminate application
179196 * if DMH_FATAL, else continue, returning the message length.
180197 */
181198 return abort_if_fatal( code,
182199 emit_and_flush(
183- fprintf( stderr, "%s: *** %s *** ", progname, severity[code] )
200+ fprintf( stderr, notification_format, progname, severity_tag( code ) )
184201 + vfprintf( stderr, fmt, argv )
185202 )
186203 );
@@ -196,9 +213,21 @@ int dmhTypeTTY::printf( const char *fmt, va_list argv )
196213
197214 EXTERN_C uint16_t dmh_control( const uint16_t request, const uint16_t mask )
198215 {
216+ /* Public helper to access and manipulate the control channel for
217+ * the diagnostic message handler.
218+ */
199219 return dmh->control( request, mask );
200220 }
201221
222+EXTERN_C void dmh_setpty( HWND console )
223+{
224+ /* Public API for assignment of a GUI window as a pseudo-console
225+ * for streaming of diagnostic message handler output; ("console"
226+ * is assumed to refer to a Windows EDITTEXT control).
227+ */
228+ dmh->set_console_hook( (void *)(console) );
229+}
230+
202231 EXTERN_C int dmh_notify( const dmh_severity code, const char *fmt, ... )
203232 {
204233 /* Public entry point for diagnostic message dispatcher.
--- a/src/dmhcore.h
+++ b/src/dmhcore.h
@@ -36,12 +36,15 @@ class dmhTypeGeneric
3636 */
3737 public:
3838 dmhTypeGeneric( const char* );
39+ virtual void set_console_hook( void * ){}
3940 virtual uint16_t control( const uint16_t, const uint16_t ) = 0;
4041 virtual int notify( const dmh_severity, const char*, va_list ) = 0;
4142 virtual int printf( const char*, va_list ) = 0;
4243
4344 protected:
4445 const char *progname;
46+ static const char *severity_tag( dmh_severity );
47+ static const char *notification_format;
4548 };
4649
4750 #endif /* DMHCORE_H: $RCSfile$: end of file */
--- a/src/guidmh.cpp
+++ b/src/guidmh.cpp
@@ -34,6 +34,217 @@
3434 #define WIN32_LEAN_AND_MEAN
3535 #include <windows.h>
3636
37+/* Establish limits on the buffer size for any EDITTEXT control which is
38+ * used to emulate the VDU device of a pseudo-TTY diagnostic console.
39+ */
40+#define DMH_PTY_MIN_BUFSIZ 4096
41+#define DMH_PTY_MAX_BUFSIZ 32768
42+
43+class dmhTypePTY
44+{
45+ /* An auxiliary class, which may be associated with a DMH controller,
46+ * to control the emulated VDU device of a pseudo-TTY console.
47+ */
48+ public:
49+ dmhTypePTY( HWND );
50+ int printf( const char *, va_list );
51+ ~dmhTypePTY();
52+
53+ private:
54+ HWND console_hook;
55+ static HFONT console_font;
56+ char *console_buffer, *caret; size_t max;
57+ int putchar( int );
58+};
59+
60+/* When we assign an emulated PTY display to a DMH data stream, in a
61+ * GUI context, we will wish to also assign a monospaced font object,
62+ * (Lucida Console, for preference). We will create this font object
63+ * once, on making the first PTY assignment, and will maintain this
64+ * static reference to locate it; we initialise the reference to NULL,
65+ * indicating the requirement to create the font object.
66+ */
67+HFONT dmhTypePTY::console_font = NULL;
68+
69+static int CALLBACK dmhWordBreak( const char *buf, int cur, int max, int op )
70+{
71+ /* This trivial word-break call-back is assigned in place of the default
72+ * handler for any EDITTEXT control used as a pseudo-TTY; it emulates the
73+ * behaviour of a dumb VT-100 display, wrapping text at the right extent
74+ * of the "screen", regardless of proximity to any natural word-break.
75+ *
76+ * Note that this call-back function must conform to the prototype which
77+ * has been specified by Microsoft, even though it uses neither the "buf"
78+ * nor the "max" arguments; we simply confirm, on request, that each and
79+ * every character in "buf" may be considered as a word-break delimiter,
80+ * and that the "cur" position in "buf" is a potential word-break point.
81+ */
82+ return (op == WB_ISDELIMITER) ? TRUE : cur;
83+}
84+
85+dmhTypePTY::dmhTypePTY( HWND console ): console_hook( console )
86+{
87+ /* Construct a PTY controller and assign it to the EDITTEXT control which
88+ * we assume, without checking, has been passed as "console".
89+ */
90+ if( console_font == NULL )
91+ {
92+ /* The font object for use with PTY diagnostic streams has yet to be
93+ * created; create it now.
94+ */
95+ LOGFONT font_info;
96+ console_font = (HFONT)(SendMessage( console, WM_GETFONT, 0, 0 ));
97+ GetObject( console_font, sizeof( LOGFONT ), &font_info );
98+ strcpy( (char *)(&(font_info.lfFaceName)), "Lucida Console" );
99+ font_info.lfHeight = (font_info.lfHeight * 9) / 10;
100+ font_info.lfWidth = (font_info.lfWidth * 9) / 10;
101+ console_font = CreateFontIndirect( &font_info );
102+ }
103+ if( console_font != NULL )
104+ /*
105+ * When we have a valid font object, we instruct the EDITTEXT control
106+ * to use it, within its device context.
107+ */
108+ SendMessage( console, WM_SETFONT, (WPARAM)(console_font), TRUE );
109+
110+ /* Override the default EDITTEXT word-wrapping behaviour, so that we
111+ * more accurately emulate the display behaviour of a VT-100 device.
112+ */
113+ SendMessage( console, EM_SETWORDBREAKPROC, 0, (LPARAM)(dmhWordBreak) );
114+
115+ /* Finally, establish a working buffer for output to the PTY, and set
116+ * the initial caret position for the output text stream.
117+ */
118+ caret = console_buffer = (char *)(malloc( max = DMH_PTY_MIN_BUFSIZ ));
119+}
120+
121+int dmhTypePTY::printf( const char *fmt, va_list argv )
122+{
123+ /* Handler for printf() style output to a DMH pseudo-TTY stream.
124+ *
125+ * We begin by formatting the specified arguments, while saving
126+ * the result into a local transfer buffer; (as a side effect,
127+ * this also sets the output character count)...
128+ */
129+ char buf[1 + vsnprintf( NULL, 0, fmt, argv )];
130+ int retval = vsnprintf( buf, sizeof( buf ), fmt, argv );
131+ for( char *bufptr = buf; *bufptr; ++bufptr )
132+ {
133+ /* We transfer the content of the local buffer to the "device"
134+ * buffer; (we do this character by character, because the DMH
135+ * output stream encodes line breaks as God intended -- using
136+ * '\n' only -- but the EDITTEXT control which emulates the PTY
137+ * "device" requires Lucifer's "\r\n" encoding; thus '\r' and
138+ * '\n' require special handling).
139+ */
140+ if( *bufptr == '\r' )
141+ {
142+ /* An explicit '\r' in the data stream should return the
143+ * caret to the start of the current PHYSICAL line in the
144+ * EDITTEXT display
145+ *
146+ * FIXME: must implement this; ignore, (and discount), it
147+ * for now.
148+ */
149+ --retval;
150+ }
151+ else
152+ { /* Any other character is transferred to the "device" buffer...
153+ */
154+ if( *bufptr == '\n' )
155+ {
156+ /* ...inserting, (and counting), an implied '\r' before each
157+ * '\n', to comply with Lucifer's encoding standard.
158+ */
159+ putchar( '\r' );
160+ ++retval;
161+ }
162+ putchar( *bufptr );
163+ }
164+ }
165+ /* When all output has been transferred to the "device" buffer, we
166+ * terminate and flush it to the EDITTEXT control.
167+ */
168+ *caret = '\0';
169+ SendMessage( console_hook, EM_SETSEL, 0, (LPARAM)(-1) );
170+ SendMessage( console_hook, EM_REPLACESEL, FALSE, (LPARAM)(console_buffer) );
171+
172+ /* Finally, we repaint the display window, and return the output
173+ * character count.
174+ */
175+ InvalidateRect( console_hook, NULL, FALSE );
176+ UpdateWindow( console_hook );
177+ return retval;
178+}
179+
180+int dmhTypePTY::putchar( int charval )
181+{
182+ /* Helper method for appending a single character to the PTY "device"
183+ * buffer; this will allocate an expanding buffer, (up to the maximum
184+ * specified size limit), as may be required.
185+ */
186+ size_t offset;
187+ if( (offset = caret - console_buffer) >= max )
188+ {
189+ /* The current "device" buffer is full; compute a new size, for
190+ * possible buffer expansion.
191+ */
192+ size_t newsize;
193+ if( (newsize = max + DMH_PTY_MIN_BUFSIZ) > DMH_PTY_MAX_BUFSIZ )
194+ newsize = DMH_PTY_MAX_BUFSIZ;
195+
196+ if( newsize > max )
197+ {
198+ /* The buffer has not yet grown to its maximum allowed size;
199+ * attempt to allocate additional memory, to expand it.
200+ */
201+ char *newbuf;
202+ if( (newbuf = (char *)(realloc( console_buffer, newsize ))) != NULL )
203+ {
204+ /* Allocation was successful; complete assignment of the new
205+ * buffer, and adjust the caret to preserve its position, at
206+ * its original offset within the new buffer.
207+ */
208+ max = newsize;
209+ caret = (console_buffer = newbuf) + offset;
210+ }
211+ }
212+ if( offset >= max )
213+ {
214+ /* The buffer has reached its maximum permitted size, (or there
215+ * was insufficient free memory to expand it), and there still
216+ * isn't room to accommodate another character; locate the start
217+ * of the SECOND logical line within it...
218+ */
219+ char *mark = console_buffer, *endptr = caret;
220+ while( *mark && (mark < caret) && (*mark++ != '\n') )
221+ ;
222+ /* ...then copy it, and all following lines, to the start of the
223+ * buffer, so deleting the FIRST logical line, and thus free up
224+ * an equivalent amount of space at the end.
225+ */
226+ for( caret = console_buffer; mark < endptr; )
227+ *caret++ = *mark++;
228+ }
229+ }
230+ /* Finally, store the current character into the "device" buffer, and
231+ * adjust the caret position in readiness for the next.
232+ */
233+ return *caret++ = charval;
234+}
235+
236+dmhTypePTY::~dmhTypePTY()
237+{
238+ /* When closing a PTY controller handle, we may free the memory which
239+ * we allocated for its "device" buffer. (Note that we DO NOT delete
240+ * the assocaiated font object, because the associated EDITTEXT control
241+ * may outlive its controller, and will continue to need it; however,
242+ * it does not need the buffer, because it maintains its own private
243+ * copy of the content).
244+ */
245+ free( (void *)(console_buffer) );
246+}
247+
37248 class dmhTypeGUI: public dmhTypeGeneric
38249 {
39250 /* Diagnostic message handler for use in GUI applications.
@@ -43,11 +254,13 @@ class dmhTypeGUI: public dmhTypeGeneric
43254 virtual uint16_t control( const uint16_t, const uint16_t );
44255 virtual int notify( const dmh_severity, const char*, va_list );
45256 virtual int printf( const char*, va_list );
257+ virtual void set_console_hook( void * );
46258
47259 private:
48260 HWND owner; uint16_t mode;
49261 char *msgbuf; size_t msglen;
50262 int dispatch_message( int );
263+ dmhTypePTY *console_hook;
51264 };
52265
53266 EXTERN_C
@@ -67,9 +280,14 @@ dmhTypeGeneric *dmh_init_gui( const char *progname )
67280 dmhTypeGUI::dmhTypeGUI( const char* name ):dmhTypeGeneric( name ),
68281 /*
69282 * This handler also requires initialisation of the
70- * message box content collector.
283+ * message box content collector...
284+ */
285+ owner( NULL ), mode( 0 ), msgbuf( NULL ), msglen( 0 ),
286+ /*
287+ * ...and must set the default state for pseudo-console
288+ * association to "none".
71289 */
72- owner( NULL ), mode( 0 ), msgbuf( NULL ), msglen( 0 ){}
290+ console_hook( NULL ){}
73291
74292 uint16_t dmhTypeGUI::control( const uint16_t request, const uint16_t mask )
75293 {
@@ -84,11 +302,32 @@ uint16_t dmhTypeGUI::control( const uint16_t request, const uint16_t mask )
84302 return mode = request | (mode & mask);
85303 }
86304
305+void dmhTypeGUI::set_console_hook( void *console )
306+{
307+ /* Helper method to assign an emulated PTY "device" to the DMH stream;
308+ * (note that any prior PTY assignment is first discarded).
309+ */
310+ delete console_hook;
311+ console_hook = (console == NULL) ? NULL : new dmhTypePTY( (HWND)(console) );
312+}
313+
87314 int dmhTypeGUI::notify( const dmh_severity code, const char *fmt, va_list argv )
88315 {
89316 /* Message dispatcher for GUI applications; this formats messages
90317 * for display within a conventional MessageBox dialogue.
91318 */
319+ if( console_hook != NULL )
320+ {
321+ /* When the DMH stream has an associated PTY, then we direct this
322+ * notification to it, analogously to the CLI use of a TTY.
323+ */
324+ return dmh_printf( notification_format, progname, severity_tag( code ) )
325+ + console_hook->printf( fmt, argv );
326+ }
327+
328+ /* Conversely, when there is no associated PTY, then we lay out the
329+ * notification for display in a windows message box.
330+ */
92331 int newlen = 1 + vsnprintf( NULL, 0, fmt, argv );
93332 if( mode & DMH_COMPILE_DIGEST )
94333 {
@@ -200,11 +439,17 @@ int dmhTypeGUI::dispatch_message( int len )
200439 int dmhTypeGUI::printf( const char *fmt, va_list argv )
201440 {
202441 /* Display arbitrary text messages via the diagnostic message handler.
203- *
204- * FIXME: this is a stub; ideally, we would like to emulate
205- * console behaviour for displaying the message stream, but
206- * until a formal implementation can be provided, we simply
207- * emit each message in its own informational MessageBox.
442+ */
443+ if( console_hook != NULL )
444+ /*
445+ * When the DMH stream has an associated PTY, then we simply
446+ * emit the message via that.
447+ */
448+ return console_hook->printf( fmt, argv );
449+
450+ /* Conversely, when there is no associated PTY, we redirect the
451+ * message for display in a windows message box, in the guise of
452+ * a DMH_INFO notification.
208453 */
209454 return notify( DMH_INFO, fmt, argv );
210455 }