PLplot  5.15.0
wingdi.c
Go to the documentation of this file.
1 // PLplot WIN32 GDI driver.
2 //
3 // Copyright (C) 2004 Andrew Roach
4 //
5 // This file is part of PLplot.
6 //
7 // PLplot is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU Library General Public License as published
9 // by the Free Software Foundation; either version 2 of the License, or
10 // (at your option) any later version.
11 //
12 // PLplot is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Library General Public License for more details.
16 //
17 // You should have received a copy of the GNU Library General Public License
18 // along with PLplot; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 //
21 //
22 #include "plDevs.h"
23 
24 #ifdef PLD_wingdi
25 
26 #include <string.h>
27 #include <windows.h>
28 #include <windowsx.h> // GET_X_LPARAM/GET_Y_LPARAM
29 #include <commctrl.h> // For status bars
30 #if !defined ( __CYGWIN__ )
31 #include <tchar.h>
32 #else
33 #include <winnt.h>
34 #define _T( a ) __TEXT( a )
35 #endif
36 
37 #define NEED_PLDEBUG
38 #define DEBUG
39 #include "plplotP.h"
40 #include "drivers.h"
41 #include "plevent.h"
42 
43 #define ARRAY_SIZE( x ) ( ( sizeof x ) / ( sizeof *x ) )
44 #define INITIAL_HEAP_SIZE 16384 // Initial size of heap in bytes
45 
46 // Driver viewer types
47 enum _dev_viewer
48 {
49  VIEWER_MINIMAL = 0, // Display just a plot area window
50  VIEWER_FULL, // Display the full function window
51  VIEWER_PLOT // A child only plot area
52 };
53 
54 // Enumerated type for the device states
55 enum _dev_state
56 {
57  DEV_WAITING = 0, // Device is idle
58  DEV_ACTIVE, // Device is ready for next plot
59  DEV_SIZEMOVE, // Device might be sizing or moving the window
60  DEV_RESIZE, // Device is resizing the window
61  DEV_DRAWING // Device is actively drawing
62 };
63 
64 // Enumerated type used to indicate the device type
65 // for updating page metrics
66 enum _dev_type
67 {
68  DEV_WINDOW, // Setup page metrics for a window
69  DEV_PRINTER // Setup page metrics for a printer
70 };
71 
72 // Data structure used to track the fonts that are created.
73 // This avoids the overhead of recreating the font everytime
74 // a string is rendered.
75 struct _font_entry
76 {
77  LOGFONT info;
78  HDC hdc; // Device context used to create font
79  HFONT font;
80  struct _font_entry *next; // Next entry in the font list
81 };
82 
83 // Device-specific info per stream
84 struct wingdi_Dev
85 {
86  //
87  // Members that are common to interactive GUI devices
88  //
89  PLFLT xdpmm; // Device x pixel per mm
90  PLFLT ydpmm; // Device y pixel per mm
91  PLFLT xscale; // Virtual x pixels to device pixel scaling
92  PLFLT yscale; // Virtual y pixels to device pixel scaling
93  PLINT width; // Window Width (which can change)
94  PLINT height; // Window Height
95 
96  enum _dev_viewer viewer;
97  enum _dev_type type;
98  enum _dev_state state; // Current state of the device
99  enum _dev_state prev_state; // Previous state of the device
100  // Used to restore after redraw
101  union
102  {
103  unsigned int status_bar : 1;
104  unsigned int menu_bar : 1;
105  } feature;
106 
107  //
108  // WIN32 API variables
109  //
110  HDC hdc; // Plot window device context
111  HPEN pen; // Current pen used for drawing
112  COLORREF color; // Current color
113  HDC hdc_bmp; // Bitmap device context
114  HBITMAP bitmap; // Bitmap of current display
115  HWND frame; // Handle for the main window.
116  HWND plot; // Handle for the plot area
117  HWND status_bar; // Handle for the status bar
118 
119  //
120  // Image rasterization variables
121  HDC save_hdc; // Save the original plot window DC
122  HBITMAP raster_bmp; // Bitmap for the raster image
123  RECT raster_rect; // Location of the image
124 };
125 
126 // Device info
127 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_wingdi = "wingdi:Windows GDI:1:wingdi:11:wingdi\n";
128 
129 // Counter used to indicate the number of streams in use
130 static int wingdi_streams = 0;
131 
132 // Window class for the overall frame
133 static TCHAR * frame_window_class_name = _T( "PLplotFrame" );
134 static WNDCLASSEX frame_window_class;
135 
136 // Window class for the plot area
137 static TCHAR * plot_area_class_name = _T( "PLplotArea" );
138 static WNDCLASSEX plot_area_class;
139 
140 // Popup menu used by a plot window
141 static HMENU plot_popup_menu;
142 
143 // Private heap used to allocate memory for the driver
144 static HANDLE wingdi_heap;
145 
146 // Font tracking list
147 struct _font_entry *font_list = NULL;
148 
150 void plD_init_wingdi( PLStream * );
151 
152 //--------------------------------------------------------------------------
153 // Graphics primitive implementation functions
154 //--------------------------------------------------------------------------
155 static void plD_line_wingdi( PLStream *, short, short, short, short );
156 static void plD_polyline_wingdi( PLStream *, short *, short *, PLINT );
157 static void plD_fill_polygon_wingdi( PLStream *pls );
158 static void plD_clear_wingdi( PLStream *pls,
159  PLINT x1, PLINT y1, PLINT x2, PLINT y2 );
160 static void plD_eop_wingdi( PLStream * );
161 static void plD_bop_wingdi( PLStream * );
162 static void plD_tidy_wingdi( PLStream * );
163 static void plD_wait_wingdi( PLStream * );
164 static void plD_state_wingdi( PLStream *, PLINT );
165 static void plD_esc_wingdi( PLStream *, PLINT, void * );
166 
167 enum commands
168 {
169  CommandPrint = 0x08A1,
170  CommandNextPage = 0x08A2,
171  CommandQuit = 0x08A3
172 };
173 #define PlotAreaId 0x08F0
174 #define StatusBarId 0x08F1
175 
177 {
178 #ifndef ENABLE_DYNDRIVERS
179  pdt->pl_MenuStr = "Win32/64 GDI device";
180  pdt->pl_DevName = "wingdi";
181 #endif
183  pdt->pl_seq = 11;
184  pdt->pl_init = (plD_init_fp) plD_init_wingdi;
185  pdt->pl_line = (plD_line_fp) plD_line_wingdi;
186  pdt->pl_polyline = (plD_polyline_fp) plD_polyline_wingdi;
187  pdt->pl_eop = (plD_eop_fp) plD_eop_wingdi;
188  pdt->pl_bop = (plD_bop_fp) plD_bop_wingdi;
189  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_wingdi;
190  pdt->pl_state = (plD_state_fp) plD_state_wingdi;
191  pdt->pl_esc = (plD_esc_fp) plD_esc_wingdi;
192  pdt->pl_wait = (plD_wait_fp) plD_wait_wingdi;
193 }
194 
195 static HCURSOR
196 CrossHairCursor( struct wingdi_Dev * dev )
197 {
198  HCURSOR cursor;
199 
200  cursor = LoadCursor( NULL, IDC_CROSS );
201  SetClassLongPtr( dev->plot, GCL_HCURSOR, (long) cursor );
202  return SetCursor( cursor );
203 }
204 
205 static void
206 NormalCursor( struct wingdi_Dev * dev )
207 {
208  HCURSOR cursor;
209 
210  cursor = LoadCursor( NULL, IDC_ARROW );
211  SetClassLongPtr( dev->plot, GCL_HCURSOR, (LONG_PTR) cursor );
212  SetCursor( cursor );
213 }
214 
215 static void
216 BusyCursor( struct wingdi_Dev * dev )
217 {
218  HCURSOR cursor;
219 
220  cursor = LoadCursor( NULL, IDC_WAIT );
221  SetClassLongPtr( dev->plot, GCL_HCURSOR, (LONG_PTR) cursor );
222  SetCursor( cursor );
223 }
224 
225 static void
226 update_status_bar( struct wingdi_Dev * dev )
227 {
228  LPSTR status_text[] =
229  {
230  TEXT( "Waiting" ),
231  TEXT( "Active" ),
232  TEXT( "Size/Move" ),
233  TEXT( "Resize" ),
234  TEXT( "Drawing" )
235  };
236 
237  if ( dev->status_bar == NULL )
238  return;
239 
240  SendMessage( dev->status_bar,
241  SB_SETTEXT,
242  (WPARAM) 0,
243  (LPARAM) status_text[dev->state] );
244 }
245 
246 //--------------------------------------------------------------------------
247 // static void UpdatePageMetrics ( PLStream *pls, char flag )
248 //
249 // UpdatePageMetrics is a simple function which simply gets new values
250 // for a changed DC, be it swapping from printer to screen or vice-versa.
251 // The flag variable is used to tell the function if it is updating
252 // from the printer (1) or screen (0).
253 //--------------------------------------------------------------------------
254 static void UpdatePageMetrics( PLStream *pls, enum _dev_type dev_type )
255 {
256  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
257 
258  dev->type = dev_type;
259  if ( dev_type == DEV_PRINTER )
260  {
261  // Get the page size from the printer
262  PLINT vsize, hsize;
263  PLFLT plot_aspect, print_aspect;
264  hsize = GetDeviceCaps( dev->hdc, HORZRES );
265  vsize = GetDeviceCaps( dev->hdc, VERTRES );
266 
267  // Determine which orientation best matches the aspect
268  // ratio of the plot window
269  plot_aspect = (PLFLT) dev->width / (PLFLT) dev->height;
270  print_aspect = (PLFLT) hsize / (PLFLT) vsize;
271  if ( plot_aspect > 1.0 )
272  {
273  // Wider than tall
274  dev->width = hsize;
275  dev->height = (PLINT) ( (PLFLT) hsize / plot_aspect );
276  }
277  else
278  {
279  // Taller than wide
280  dev->width = (PLINT) ( (PLFLT) vsize * plot_aspect );
281  dev->height = vsize;
282  }
283  }
284  else
285  {
286  RECT rect;
287 
288  GetClientRect( dev->plot, &rect );
289  dev->width = rect.right;
290  dev->height = rect.bottom;
291 
292  pldebug( "wingdi", "Page size [%d %d] [%d %d]\n",
293  rect.left, rect.top,
294  rect.right, rect.bottom );
295  }
296 
297  // We need the -1 because some of the coordinates
298  // are signed and PIXEL_X/PIXEL_Y can exceed the
299  // maximum value of a positive signed integer, which
300  // results in a negative value.
301  dev->xscale = (PLFLT) ( PIXELS_X - 1 ) / dev->width;
302  dev->yscale = (PLFLT) ( PIXELS_Y - 1 ) / dev->height;
303 
304  pldebug( "wingdi", "Scale = (%f %f) (FLT)\n",
305  dev->xscale, dev->yscale );
306 
307  // Need to get the DPI information from Windows
308  // HORZRES/VERTRES = Width/Height in pixels
309  // HORZSIZE/VERTSIZE = Width/Height in millimeters
310  pldebug( "wingdi", "Original xdpi = %f ydpi = %f\n",
311  pls->xdpi, pls->ydpi );
312  //dev->xdpmm = GetDeviceCaps( dev->hdc, HORZRES )
313  // / GetDeviceCaps( dev->hdc, HORZSIZE );
314  //pls->xdpi = dev->xdpmm * 25.4;
315  //dev->ydpmm = GetDeviceCaps( dev->hdc, VERTRES )
316  // / GetDeviceCaps( dev->hdc, VERTSIZE );
317  //pls->ydpi = dev->ydpmm * 25.4;
318  pls->xdpi = GetDeviceCaps( dev->hdc, LOGPIXELSX );
319  dev->xdpmm = pls->xdpi / 25.4;
320  pls->ydpi = GetDeviceCaps( dev->hdc, LOGPIXELSY );
321  dev->ydpmm = pls->ydpi / 25.4;
322 
323  pldebug( "wingdi", "New xdpi = %f ydpi = %f\n",
324  pls->xdpi, pls->ydpi );
325  pldebug( "wingdi", "Windows reports xdpi = %d ydpi = %d\n",
326  GetDeviceCaps( dev->hdc, LOGPIXELSX ),
327  GetDeviceCaps( dev->hdc, LOGPIXELSY ) );
328 
329  // Set the mapping from pixels to mm
330  plP_setpxl( dev->xscale * pls->xdpi / 25.4,
331  dev->yscale * pls->ydpi / 25.4 );
332 
333  // Set the physical limits for this device. See the
334  // previous comment about the -1.
335  plP_setphy( 0, PIXELS_X - 1,
336  0, PIXELS_Y - 1 );
337 }
338 
339 //--------------------------------------------------------------------------
340 // static void PrintPage ( PLStream *pls )
341 //
342 // Function brings up a standard printer dialog and, after the user
343 // has selected a printer, replots the current page to the windows
344 // printer.
345 //--------------------------------------------------------------------------
346 static void PrintPage( PLStream *pls )
347 {
348  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
349  PRINTDLGEX Printer;
350  DOCINFO docinfo;
351  DEVMODE * hDevMode;
352  struct wingdi_Dev *push; // A copy of the entire structure
353 
354  // Reset the docinfo structure to 0 and set it's fields up
355  // This structure is used to supply a name to the print queue
356  ZeroMemory( &docinfo, sizeof ( docinfo ) );
357  docinfo.cbSize = sizeof ( docinfo );
358  docinfo.lpszDocName = _T( "Plplot Page" );
359 
360  // Set the defaults for the printer device
361  // Allocate a moveable block of memory. Must use GlobalAlloc because
362  // HeapAlloc is not moveable.
363  hDevMode = GlobalAlloc( GHND, sizeof ( DEVMODE ) );
364  if ( hDevMode == NULL )
365  {
366  plwarn( "wingdi: Failed to allocate memory for printer defaults\n" );
367  return;
368  }
369  ZeroMemory( hDevMode, sizeof ( DEVMODE ) );
370  hDevMode->dmSpecVersion = DM_SPECVERSION;
371  hDevMode->dmSize = sizeof ( DEVMODE );
372  hDevMode->dmFields = DM_ORIENTATION;
373  hDevMode->dmOrientation = DMORIENT_LANDSCAPE;
374 
375  // Reset out printer structure to zero and initialise it
376  ZeroMemory( &Printer, sizeof ( PRINTDLGEX ) );
377  Printer.lStructSize = sizeof ( PRINTDLGEX );
378  Printer.hwndOwner = dev->plot;
379  Printer.hDevMode = hDevMode;
380  // Disable page ranges, default to collated output,
381  // and return the device context (used to generate the output)
382  Printer.Flags = PD_NOPAGENUMS | PD_NOCURRENTPAGE | PD_NOSELECTION
383  | PD_COLLATE | PD_RETURNDC;
384  // Currently, page ranges is disabled. This code
385  // is left as a placeholder in case print ranges
386  // is allowed in the future. There is no mechanism
387  // implemented that facilitates the user interaction
388  // on selecting pages, so it is best to turn it off
389  // for now.
390  Printer.nPageRanges = 0;
391  Printer.nMaxPageRanges = 0;
392  Printer.lpPageRanges = NULL;
393  Printer.nMinPage = 0;
394  Printer.nMaxPage = 0;
395  // Other print parameter defaults
396  Printer.nCopies = 1;
397  //Printer.nPropertyPages = ARRAY_SIZE( hPrintPropSheetList ),
398  //Printer.lphPropertyPages = hPrintPropSheetList;
399  Printer.nStartPage = START_PAGE_GENERAL;
400 
401  // Call the printer dialog function.
402  // If the user has clicked on "Print", then we will continue
403  // processing and print out the page.
404  if ( PrintDlgEx( &Printer ) == S_OK
405  && Printer.dwResultAction == PD_RESULT_PRINT )
406  {
407  // Before doing anything, we will take some backup copies
408  // of the existing values for page size and the like, because
409  // all we are going to do is a quick and dirty modification
410  // of plplot's internals to match the new page size and hope
411  // it all works out ok. After that, we will manip the values,
412  // and when all is done, restore them.
413  push = HeapAlloc( wingdi_heap, 0, sizeof ( struct wingdi_Dev ) );
414  if ( push != NULL )
415  {
416  BusyCursor( dev );
417 
418  // Save all the state information of this device
419  memcpy( push, dev, sizeof ( struct wingdi_Dev ) );
420 
421  // Change the device context to the printer
422  dev->hdc = Printer.hDC;
423  UpdatePageMetrics( pls, DEV_PRINTER );
424 
425  // Now the stuff that actually does the printing !!
426  StartDoc( dev->hdc, &docinfo );
427  plRemakePlot( pls );
428  EndDoc( dev->hdc );
429 
430  // Now to undo everything back to what it was for the screen
431  memcpy( dev, push, sizeof ( struct wingdi_Dev ) );
432  UpdatePageMetrics( pls, DEV_WINDOW );
433 
434  HeapFree( wingdi_heap, 0, push );
435  NormalCursor( dev );
436 
437  // Force a redraw to make sure the plot area is clean of
438  // the leftovers from the print menu
439  //RedrawWindow( dev->plot,
440  // NULL, NULL,
441  // RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW );
442  }
443  else
444  {
445  plwarn( "wingdi: Unable to save state for print" );
446  }
447  }
448 
449  // Cleanup after printing
450  if ( Printer.hDC != NULL )
451  DeleteDC( Printer.hDC );
452  if ( Printer.hDevMode != NULL )
453  DeleteDC( Printer.hDevMode );
454  if ( Printer.hDevNames != NULL )
455  DeleteDC( Printer.hDevNames );
456  // Free allocated memory
457  if ( hDevMode )
458  GlobalFree( hDevMode );
459 }
460 
461 static void
462 wait_for_user_input( PLStream *pls )
463 {
464  struct wingdi_Dev * dev = (struct wingdi_Dev *) pls->dev;
465  MSG msg;
466 
467  pldebug( "wingdi", "Waiting for user input\n" );
468 
469  // Update the state and the message in the status bar
470  dev->state = DEV_WAITING;
471  update_status_bar( dev );
472 
473  // Process messages in the queue or until we are no longer waiting
474  while ( GetMessage( &msg, NULL, 0, 0 ) && dev->state != DEV_ACTIVE )
475  {
476  TranslateMessage( &msg );
477  switch ( (int) msg.message )
478  {
479  case WM_CONTEXTMENU:
480  case WM_RBUTTONDOWN:
481  TrackPopupMenu( plot_popup_menu,
482  TPM_CENTERALIGN | TPM_RIGHTBUTTON,
483  LOWORD( msg.lParam ),
484  HIWORD( msg.lParam ),
485  0,
486  dev->plot,
487  NULL );
488  break;
489 
490  case WM_CHAR:
491  if ( ( (TCHAR) ( msg.wParam ) == 32 ) ||
492  ( (TCHAR) ( msg.wParam ) == 13 ) )
493  {
494  dev->state = DEV_ACTIVE;
495  update_status_bar( dev );
496  }
497  else if ( ( (TCHAR) ( msg.wParam ) == 27 ) ||
498  ( (TCHAR) ( msg.wParam ) == 'q' ) ||
499  ( (TCHAR) ( msg.wParam ) == 'Q' ) )
500  {
501  dev->state = DEV_ACTIVE;
502  update_status_bar( dev );
503  PostQuitMessage( 0 );
504  }
505  break;
506 
507  case WM_LBUTTONDBLCLK:
508  pldebug( "wingdi", "WM_LBUTTONDBLCLK\n" );
509  dev->state = DEV_ACTIVE;
510  update_status_bar( dev );
511  break;
512 
513  case WM_COMMAND:
514  switch ( LOWORD( msg.wParam ) )
515  {
516  case CommandPrint:
517  pldebug( "wingdi", "CommandPrint\n" );
518  PrintPage( pls );
519  break;
520  case CommandNextPage:
521  pldebug( "wingdi", "CommandNextPage\n" );
522  dev->state = DEV_ACTIVE;
523  update_status_bar( dev );
524  break;
525  case CommandQuit:
526  pldebug( "wingdi", "CommandQuit\n" );
527  dev->state = DEV_ACTIVE;
528  update_status_bar( dev );
529  PostQuitMessage( 0 );
530  break;
531  }
532  break;
533 
534  default:
535  DispatchMessage( &msg );
536  break;
537  }
538  }
539 
540  pldebug( "wingdi", "Done waiting\n" );
541  dev->state = DEV_ACTIVE;
542 }
543 
544 //--------------------------------------------------------------------------
545 // GetCursorCmd()
546 //
547 // Handle events connected to selecting points (modelled after xwin)
548 //--------------------------------------------------------------------------
549 
550 static void
551 GetCursorCmd( PLStream *pls, PLGraphicsIn *gin )
552 {
553  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
554  MSG msg;
555  HCURSOR previous;
556 
557  plGinInit( gin );
558 
559  previous = CrossHairCursor( dev );
560 
561  while ( gin->pX < 0 )
562  {
563  GetMessage( &msg, NULL, 0, 0 );
564  TranslateMessage( &msg );
565  switch ( (int) msg.message )
566  {
567  case WM_LBUTTONDOWN:
568  gin->pX = msg.pt.x;
569  gin->pY = msg.pt.y;
570  gin->dX = (PLFLT) gin->pX / ( dev->width - 1 );
571  gin->dY = 1.0 - (PLFLT) gin->pY / ( dev->height - 1 );
572 
573  if ( msg.wParam & MK_LBUTTON )
574  {
575  pldebug( "wingdi", "Left button down\n" );
576  // Left button was pressed
577  gin->button = 1;
578  gin->state = 0;
579  //gin->keysym = 0x20;
580  }
581  else if ( msg.wParam & MK_MBUTTON )
582  {
583  pldebug( "wingdi", "Middle button down\n" );
584  // Middle button was pressed
585  gin->button = 3;
586  gin->state = 0;
587  }
588  else if ( msg.wParam & MK_RBUTTON )
589  {
590  pldebug( "wingdi", "Right button down\n" );
591  // Right button was pressed
592  gin->button = 2;
593  gin->state = 0;
594  }
595  break;
596 
597  case WM_LBUTTONUP:
598  gin->pX = msg.pt.x;
599  gin->pY = msg.pt.y;
600  gin->dX = (PLFLT) gin->pX / ( dev->width - 1 );
601  gin->dY = 1.0 - (PLFLT) gin->pY / ( dev->height - 1 );
602 
603  if ( msg.wParam & MK_LBUTTON )
604  {
605  pldebug( "wingdi", "Left button up\n" );
606  // Left button was pressed
607  gin->button = 1;
608  gin->state = 0x100;
609  }
610  else if ( msg.wParam & MK_MBUTTON )
611  {
612  pldebug( "wingdi", "Middle button up\n" );
613  // Middle button was pressed
614  gin->button = 3;
615  gin->state = 0x10000;
616  }
617  else if ( msg.wParam & MK_RBUTTON )
618  {
619  pldebug( "wingdi", "Right button up\n" );
620  // Right button was pressed
621  gin->button = 2;
622  gin->state = 0x1000;
623  }
624  break;
625 
626  case WM_CHAR:
627  gin->pX = msg.pt.x;
628  gin->pY = msg.pt.y;
629  gin->dX = (PLFLT) gin->pX / ( dev->width - 1 );
630  gin->dY = 1.0 - (PLFLT) gin->pY / ( dev->height - 1 );
631 
632  gin->button = 0;
633  gin->state = 0;
634  gin->keysym = msg.wParam;
635 
636  break;
637  }
638  }
639 
640  // Restore the previous cursor
641  SetCursor( previous );
642 }
643 
644 //--------------------------------------------------------------------------
645 // static void CopySCRtoBMP(PLStream *pls)
646 // Function copies the screen contents into a bitmap which is
647 // later used for fast redraws of the screen (when it gets corrupted)
648 // rather than remaking the plot from the plot buffer.
649 //--------------------------------------------------------------------------
650 static void CopySCRtoBMP( PLStream *pls )
651 {
652  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
653  RECT rect;
654  HGDIOBJ previous;
655 
656  // Delete the existing bitmap before creating a new one
657  if ( dev->hdc_bmp != NULL )
658  DeleteDC( dev->hdc_bmp );
659  if ( dev->bitmap != NULL )
660  DeleteObject( dev->bitmap );
661 
662  dev->hdc_bmp = CreateCompatibleDC( dev->hdc );
663  GetClientRect( dev->plot, &rect );
664  dev->bitmap = CreateCompatibleBitmap( dev->hdc,
665  rect.right, rect.bottom );
666  previous = SelectObject( dev->hdc_bmp, dev->bitmap );
667  BitBlt( dev->hdc_bmp, 0, 0, rect.right, rect.bottom, dev->hdc, 0, 0, SRCCOPY );
668  SelectObject( dev->hdc_bmp, previous );
669 }
670 
671 //--------------------------------------------------------------------------
672 // static void Erase ( PLStream *pls )
673 //
674 // This function erases the client area of a window
675 //--------------------------------------------------------------------------
676 static void
677 Erase( PLStream *pls )
678 {
679  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
680  COLORREF previous_color;
681  RECT rect;
682 
683  pldebug( "wingdi", " Erasing window\n" );
684  //
685  // This is a new "High Speed" way of filling in the background.
686  // supposedly this executes faster than creating a brush and
687  // filling a rectangle - go figure ?
688  //
689  if ( dev->type == DEV_WINDOW )
690  {
691  // NOTE: Should GetUpdateRect be used instead?
692  GetClientRect( dev->plot, &rect );
693  previous_color = SetBkColor( dev->hdc,
694  RGB( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b ) );
695  ExtTextOut( dev->hdc, 0, 0, ETO_OPAQUE, &rect, _T( "" ), 0, 0 );
696  SetBkColor( dev->hdc, previous_color );
697  }
698  else
699  {
700  rect.left = 0;
701  rect.top = 0;
702  rect.right = GetDeviceCaps( dev->hdc, HORZRES );
703  rect.bottom = GetDeviceCaps( dev->hdc, VERTRES );
704  }
705 }
706 
707 //--------------------------------------------------------------------------
708 // static void Resize( PLStream *pls )
709 //
710 // This function regenerates a plot by updating the page metrics and then
711 // using the plot buffer to recreate the plot. This function assumes that is
712 // being called from a WM_PAINT message, thus it does not do anything to force
713 // a redraw.
714 //--------------------------------------------------------------------------
715 static void Resize( PLStream *pls )
716 {
717  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
718  RECT rect;
719  enum _dev_state current;
720 
721  pldebug( "wingdi", "Resizing\n" );
722 
723  // Only resize the window IF plplot is not busy
724  if ( dev->state == DEV_WAITING
725  || dev->state == DEV_ACTIVE
726  || dev->state == DEV_DRAWING )
727  {
728  GetClientRect( dev->plot, &rect );
729  pldebug( "wingdi", " Size = [%d %d] [%d %d]\n",
730  rect.left, rect.top,
731  rect.right, rect.bottom );
732 
733  // Check to make sure it isn't just minimised (i.e. zero size)
734  if ( ( rect.right > 0 ) && ( rect.bottom > 0 ) )
735  {
736  UpdatePageMetrics( pls, DEV_WINDOW );
737 
738  // Save the current state because remaking the plot
739  // will change it when the BOP is executed.
740  current = dev->state;
741  plRemakePlot( pls );
742  dev->state = current;
743  update_status_bar( dev );
744  }
745  }
746  else
747  {
748  pldebug( "wingdi", " No action taken, state = %d\n", dev->state );
749  }
750  pldebug( "wingdi", "Resizing done\n" );
751 }
752 
753 //--------------------------------------------------------------------------
754 // This is the window function for the frame window.
755 //--------------------------------------------------------------------------
756 LRESULT CALLBACK PlplotFrameProc(
757  HWND hwnd, // handle to window
758  UINT uMsg, // message identifier
759  WPARAM wParam, // first message parameter
760  LPARAM lParam ) // second message parameter
761 {
762  struct wingdi_Dev *dev = NULL;
763 
764  // Try to get the address to the device data for this window
765 #ifdef _WIN64
766  dev = (struct wingdi_Dev *) GetWindowLongPtr( hwnd, GWLP_USERDATA );
767 #else
768  dev = (struct wingdi_Dev *) GetWindowLongPtr( hwnd, GWL_USERDATA );
769 #endif
770 
771  switch ( uMsg )
772  {
773  case WM_CREATE:
774  // Initialize the window.
775  {
776  HWND hStatus;
777 
778  // Create the status bar only when the frame is being created
779  hStatus = CreateWindowEx(
780  0,
781  STATUSCLASSNAME,
782  NULL,
783  WS_CHILD | WS_VISIBLE,
784  0, 0, 0, 0,
785  hwnd,
786  (HMENU) StatusBarId,
787  GetModuleHandle( NULL ),
788  NULL );
789  if ( hStatus != NULL )
790  {
791  int status_widths[] = { 100, 200, 300, -1 };
792 
793  SendMessage( hStatus, SB_SETPARTS,
794  (WPARAM) ARRAY_SIZE( status_widths ),
795  (LPARAM) status_widths );
796  SendMessage( hStatus, SB_SETTEXT,
797  (WPARAM) 0,
798  (LPARAM) (LPSTR) TEXT( "Active" ) );
799  }
800  else
801  {
802  MessageBox( hwnd, "Could not create status bar.",
803  "Error", MB_OK | MB_ICONERROR );
804  }
805  }
806  return 0;
807 
808  case WM_SIZE:
809  {
810  // Set the size and position of the window, including the
811  // child controls
812  HWND hStatus, hPlot;
813  RECT rStatus, rFrame;
814  int plot_height;
815 
816  // Get the client area of the frame
817  GetClientRect( hwnd, &rFrame );
818 
819  // Get the status bar size
820  hStatus = GetDlgItem( hwnd, StatusBarId );
821  SendMessage( hStatus, WM_SIZE, 0, 0 );
822  GetWindowRect( hStatus, &rStatus );
823 
824  // Set the size of the plot area
825  hPlot = GetDlgItem( hwnd, PlotAreaId );
826  plot_height = rFrame.bottom - ( rStatus.bottom - rStatus.top );
827  SetWindowPos( hPlot, // Handle to the plot area window
828  NULL,
829  0, 0,
830  rFrame.right, plot_height,
831  SWP_NOZORDER | SWP_SHOWWINDOW );
832  }
833  return 0;
834 
835  case WM_DESTROY:
836  // Clean up window-specific data objects.
837  return 0;
838 
839  //
840  // Process other messages.
841  //
842  default:
843  return DefWindowProc( hwnd, uMsg, wParam, lParam );
844  }
845 
846  return 0;
847 }
848 
849 //--------------------------------------------------------------------------
850 // This is the window function for the plot area. Whenever a message is
851 // dispatched using DispatchMessage (or sent with SendMessage) this function
852 // gets called with the contents of the message.
853 //--------------------------------------------------------------------------
854 LRESULT CALLBACK PlplotPlotAreaProc( HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
855 {
856  PLStream *pls = NULL;
857  struct wingdi_Dev *dev = NULL;
858 
859  // During WM_CREATE message, the PLStream pointer is not set, thus
860  // we need to handle this message without attempting to getting the user data
861  if ( nMsg == WM_CREATE )
862  {
863  // Signal that the message was handled
864  return ( 0 );
865  }
866 
867  // Try to get the address to pls for this window
868 #ifdef _WIN64
869  pls = (PLStream *) GetWindowLongPtr( hwnd, GWLP_USERDATA );
870 #else
871  pls = (PLStream *) GetWindowLongPtr( hwnd, GWL_USERDATA );
872 #endif
873 
874  // If we did not get a valid pointer, pass the message to the
875  // default message handler
876  if ( pls == NULL )
877  {
878  return DefWindowProc( hwnd, nMsg, wParam, lParam );
879  }
880 
881  dev = (struct wingdi_Dev *) pls->dev;
882 
883  //
884  // Process the windows messages
885  //
886  // Everything except WM_CREATE is done here and it is generally hoped that
887  // pls and dev are defined already by this stage.
888  // That will be true MOST of the time. Some times WM_PAINT will be called
889  // before we get to initialise the user data area of the window with the
890  // pointer to the windows plplot stream
891  //
892 
893  switch ( nMsg )
894  {
895  case WM_DESTROY:
896  pldebug( "wingdi", "WM_DESTROY\n" );
897  PostQuitMessage( 0 );
898  return ( 0 );
899  break;
900 
901  case WM_PAINT:
902  // A WM_PAINT message gets sent on an expose, resize, and move
903  // events. On expose and move events, a blit of a bitmap will
904  // be performed. On a resize, the plot needs to be regenerated.
905  // Because a WM_PAINT is sent after the triggering event
906  // (e.g. a move), the triggering event is responsible for ensuring
907  // a valid bitmap exists. Thus, this message handler only needs
908  // to blit the bitmap to the window.
909  pldebug( "wingdi", "WM_PAINT state = %d\n", dev->state );
910 
911  // Check to see if there is an area that needs to be redrawn.
912  // Per the MSDN document, calling GetUpdateRect with a NULL RECT
913  // value will determine if there is an update region. BeginPaint()
914  // will provide the update region in rcPaint.
915  if ( GetUpdateRect( dev->plot, NULL, FALSE ) )
916  {
917  // Yes there is an update region, start the redraw
918  PAINTSTRUCT ps;
919  HGDIOBJ previous;
920 
921  BusyCursor( dev );
922  BeginPaint( dev->plot, &ps );
923 
924  pldebug( "wingdi", " Need to redraw area (%d,%d) (%d,%d)\n",
925  ps.rcPaint.left, ps.rcPaint.top,
926  ps.rcPaint.right, ps.rcPaint.bottom );
927  pldebug( "wingdi", " Erase status = %d\n", ps.fErase );
928  pldebug( "wingdi", " Device state = %d\n", dev->state );
929 
930  // If we have a valid bitmap and are not currently drawing
931  // a new plot, then we can blit the bitmap to handle the
932  // redraw. On a resize, this will result in the bitmap being
933  // clipped during the resize. A StretchBlt could be used to
934  // rescale the bitmap; however, not all devices support
935  // stretch blits.
936  if ( dev->bitmap != NULL && dev->state != DEV_DRAWING )
937  {
938  // A bitmap exists, thus only a blit is required
939  pldebug( "wingdi", " Blit image\n" );
940  previous = SelectObject( dev->hdc_bmp, dev->bitmap );
941  BitBlt( dev->hdc,
942  ps.rcPaint.left, ps.rcPaint.top,
943  ps.rcPaint.right, ps.rcPaint.bottom,
944  dev->hdc_bmp,
945  ps.rcPaint.left, ps.rcPaint.top,
946  SRCCOPY );
947  SelectObject( dev->hdc_bmp, previous );
948  }
949  else
950  {
951  pldebug( "wingdi",
952  " No paint action bitmap = %lx state = \n",
953  dev->bitmap, dev->state );
954  }
955 
956  EndPaint( dev->plot, &ps );
957  NormalCursor( dev );
958  }
959  else
960  {
961  pldebug( "wingdi", " No update area to paint\n" );
962  }
963  pldebug( "wingdi", "WM_PAINT exit\n" );
964  // Signal that the message was processed
965  return ( 0 );
966  break;
967 
968  case WM_SIZE:
969  pldebug( "wingdi", "WM_SIZE wParam = %d\n", wParam );
970  // The size might have changed. We only care about
971  // Maximized events, a drag resize, or a restore.
972  if ( wParam == SIZE_MAXIMIZED )
973  {
974  // The window was maximized, which is a resize
975  pldebug( "wingdi", " Window maximized\n" );
976  Resize( pls );
977  }
978  else if ( dev->state == DEV_SIZEMOVE )
979  {
980  // This is a drag resize. Must check before the SIZE_RESTORED
981  // because a drag resize is also a SIZE_RESTORED.
982  pldebug( "wingdi", " Window size/moved\n" );
983 
984  // Change the state to indicate that the window has changed
985  // size and not just moved.
986  dev->state = DEV_RESIZE;
987  pldebug( "wingdi", " New state %d\n", dev->state );
988  update_status_bar( dev );
989  }
990  else if ( wParam == SIZE_RESTORED )
991  {
992  // This could be a restore from a maximized or minimized state.
993  // Unless code is added to detect the difference (i.e. look for
994  // an earlier SIZE_MINIMIZED), just treat it as a resize
995  pldebug( "wingdi", " Window restored\n" );
996  Resize( pls );
997  }
998  else
999  {
1000  pldebug( "wingdi", " Unknowing sizing action\n" );
1001  }
1002  pldebug( "wingdi", "WM_SIZE exit\n" );
1003  // Indicate that this message was processed
1004  return ( 0 );
1005  break;
1006 
1007  case WM_ENTERSIZEMOVE:
1008  pldebug( "wingdi", "WM_ENTERSIZEMOVE\n" );
1009  pldebug( "wingdi", " Save state %d\n", dev->state );
1010  dev->prev_state = dev->state;
1011  // Indicate that we might be sizing or moving the window
1012  dev->state = DEV_SIZEMOVE;
1013  update_status_bar( dev );
1014  return ( 0 );
1015  break;
1016 
1017  case WM_EXITSIZEMOVE:
1018  pldebug( "wingdi", "WM_EXITSIZEMOVE\n" );
1019  // If the window has been resized, regenerate the plot
1020  // for the new window dimensions
1021  if ( dev->state == DEV_RESIZE || dev->state == DEV_SIZEMOVE )
1022  {
1023  pldebug( "wingdi", " Restore state %d\n", dev->prev_state );
1024  // Restore the previous state before handling the resize
1025  // The resize routine needs the original state to preserve
1026  // the state when the buffer is replayed.
1027  dev->state = dev->prev_state;
1028  update_status_bar( dev );
1029  Resize( pls );
1030  }
1031  return ( 0 );
1032  break;
1033 
1034  case WM_ERASEBKGND:
1035  // Determine if the window needs to be erased based
1036  // on the current state.
1037  // DEV_WAITING = No erase necessary, the next WM_PAINT
1038  // will repaint the affected area
1039  pldebug( "wingdi", "WM_ERASEBKGND state = %d\n", dev->state );
1040 
1041  if ( dev->state != DEV_WAITING )
1042  {
1043  Erase( pls );
1044  // Indicate that the client area was erased
1045  return ( 1 );
1046  }
1047 
1048  // Indicate no action was taken
1049  pldebug( "wingdi", " No erase action taken\n" );
1050  return ( 0 );
1051  break;
1052 
1053  case WM_MOUSEMOVE:
1054  {
1055  char mesg[80];
1056  int xw, yw;
1057  double x, y;
1058 
1059  xw = GET_X_LPARAM( lParam );
1060  yw = GET_Y_LPARAM( lParam );
1061  x = (double) xw * dev->xscale;
1062  y = (double) ( dev->height - yw ) * dev->yscale;
1063 
1064  snprintf( mesg, sizeof ( mesg ), "%5.1lf", x );
1065  if ( dev->status_bar != NULL )
1066  SendMessage( dev->status_bar,
1067  SB_SETTEXT,
1068  (WPARAM) 1,
1069  (LPARAM) mesg );
1070 
1071  snprintf( mesg, sizeof ( mesg ), "%5.1lf", y );
1072  if ( dev->status_bar != NULL )
1073  SendMessage( dev->status_bar,
1074  SB_SETTEXT,
1075  (WPARAM) 2,
1076  (LPARAM) mesg );
1077  }
1078 
1079  // Indicate that we did not process this message
1080  return ( 1 );
1081  break;
1082 
1083  case WM_COMMAND:
1084  pldebug( "wingdi", "WM_COMMAND\n" );
1085  return ( 0 );
1086  break;
1087  }
1088 
1089  // If we don't handle a message completely we hand it to the system
1090  // provided default window function.
1091  return DefWindowProc( hwnd, nMsg, wParam, lParam );
1092 }
1093 
1094 //--------------------------------------------------------------------------
1095 // wingdi_module_initialize()
1096 //
1097 // Handles the initialization of window classes and module global
1098 // variables.
1099 //--------------------------------------------------------------------------
1100 static void
1101 wingdi_module_initialize( void )
1102 {
1103  INITCOMMONCONTROLSEX init_controls;
1104 
1105  // Return if the module has been initialized
1106  // Use a postfix increment so that wingdi_streams is incremented
1107  // every time this function is called. That ensures a valid count
1108  // for the number of streams that are created. This allows the
1109  // module cleanup to free resources when the last stream is closed.
1110  if ( wingdi_streams++ > 0 )
1111  return;
1112 
1113  pldebug( "wingdi", "module init\n" );
1114 
1115  // Initialize common controls
1116  init_controls.dwSize = sizeof ( INITCOMMONCONTROLSEX );
1117  init_controls.dwICC = ICC_BAR_CLASSES;
1118  if ( !InitCommonControlsEx( &init_controls ) )
1119  {
1120  plwarn( "wingdi: Failed to initialize common Window controls\n" );
1121  }
1122 
1123  //
1124  // Initialize the frame window class
1125  //
1126 
1127  // Initialize the entire structure to zero.
1128  memset( &frame_window_class, 0, sizeof ( WNDCLASSEX ) );
1129 
1130  // Set the name of the plot window class
1131  frame_window_class.lpszClassName = frame_window_class_name;
1132 
1133  // Set the size of the window class structure, to include
1134  // any extra data that might be passed
1135  frame_window_class.cbSize = sizeof ( WNDCLASSEX );
1136 
1137  // All windows of this class redraw when resized.
1138  frame_window_class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS
1139  | CS_OWNDC;
1140 
1141  // Set the callback function.
1142  frame_window_class.lpfnWndProc = PlplotFrameProc;
1143 
1144  // This class is used with the current program instance.
1145  frame_window_class.hInstance = GetModuleHandle( NULL );
1146 
1147  // Use standard application icon and arrow cursor provided by the OS
1148  frame_window_class.hIcon = LoadIcon( NULL, IDI_APPLICATION );
1149  frame_window_class.hIconSm = LoadIcon( NULL, IDI_APPLICATION );
1150  frame_window_class.hCursor = LoadCursor( NULL, IDC_ARROW );
1151 
1152  // Generic solid background for the frame window
1153  frame_window_class.hbrBackground = (HBRUSH) COLOR_WINDOW;
1154 
1155  // Do not allocate extra space for the callback
1156  frame_window_class.cbWndExtra = sizeof ( struct wingdi_Dev * );
1157 
1158  // Now register the window class for use.
1159  RegisterClassEx( &frame_window_class );
1160 
1161  //
1162  // Initialize the plot area class
1163  //
1164 
1165  // Initialize the entire structure to zero.
1166  memset( &plot_area_class, 0, sizeof ( WNDCLASSEX ) );
1167 
1168  // Set the name of the plot window class
1169  plot_area_class.lpszClassName = plot_area_class_name;
1170 
1171  // Set the size of the window class structure, to include
1172  // any extra data that might be passed
1173  plot_area_class.cbSize = sizeof ( WNDCLASSEX );
1174 
1175  // All windows of this class redraw when resized.
1176  plot_area_class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS
1177  | CS_OWNDC;
1178 
1179  // Set the callback function.
1180  plot_area_class.lpfnWndProc = PlplotPlotAreaProc;
1181 
1182  // This class is used with the current program instance.
1183  plot_area_class.hInstance = GetModuleHandle( NULL );
1184 
1185  // Use standard application icon and arrow cursor provided by the OS
1186  plot_area_class.hIcon = LoadIcon( NULL, IDI_APPLICATION );
1187  plot_area_class.hIconSm = LoadIcon( NULL, IDI_APPLICATION );
1188  plot_area_class.hCursor = LoadCursor( NULL, IDC_ARROW );
1189 
1190  // Handle the erase background in the callback function
1191  plot_area_class.hbrBackground = NULL;
1192 
1193  // Allocate extra space for a window instance to store the
1194  // pointer to the plot stream (PLStream)
1195  plot_area_class.cbWndExtra = sizeof ( PLStream * );
1196 
1197  // Now register the window class for use.
1198  RegisterClassEx( &plot_area_class );
1199 
1200  //
1201  // Create the popup menu used by the plot window
1202  //
1203  plot_popup_menu = CreatePopupMenu();
1204  AppendMenu( plot_popup_menu, MF_STRING, CommandPrint, _T( "Print" ) );
1205  AppendMenu( plot_popup_menu, MF_STRING, CommandNextPage, _T( "Next Page" ) );
1206  AppendMenu( plot_popup_menu, MF_STRING, CommandQuit, _T( "Quit" ) );
1207 
1208  //
1209  // Create a private heap to use for memory allocation.
1210  // This will keep memory separate from the overall program
1211  // and does not have the overhead of GlobalAlloc(). Requires
1212  // a minimum of Windows XP or Windows Server 2003
1213  //
1214  wingdi_heap = HeapCreate( 0, INITIAL_HEAP_SIZE, 0 );
1215  if ( wingdi_heap == NULL )
1216  {
1217  //plexit("wingdi: Unable to allocate heap of size %d bytes",
1218  // INITIAL_HEAP_SIZE);
1219  plexit( "wingdi: Unable to allocate heap" );
1220  }
1221 }
1222 
1223 static void wingdi_module_cleanup( void )
1224 {
1225  struct _font_entry *ptr;
1226 
1227  wingdi_streams--;
1228  if ( wingdi_streams > 0 )
1229  return;
1230 
1231  DeleteMenu( plot_popup_menu, CommandPrint, 0 );
1232  DeleteMenu( plot_popup_menu, CommandNextPage, 0 );
1233  DeleteMenu( plot_popup_menu, CommandQuit, 0 );
1234  DestroyMenu( plot_popup_menu );
1235 
1236  if ( !UnregisterClass( plot_area_class_name, plot_area_class.hInstance ) )
1237  {
1238  plexit( "wingdi: Failed to unregister window class" );
1239  }
1240  if ( !UnregisterClass( frame_window_class_name, frame_window_class.hInstance ) )
1241  {
1242  plexit( "wingdi: Failed to unregister window class" );
1243  }
1244 
1245  while ( font_list != NULL )
1246  {
1247  ptr = font_list;
1248  DeleteObject( ptr->font );
1249  font_list = ptr->next;
1250  HeapFree( wingdi_heap, 0, ptr );
1251  }
1252 
1253  if ( HeapDestroy( wingdi_heap ) == 0 )
1254  {
1255  plexit( "wingdi: Failed to destroy heap" );
1256  }
1257 }
1258 
1259 //--------------------------------------------------------------------------
1260 // plD_init_wingdi()
1261 //
1262 // Initialize device (terminal).
1263 //--------------------------------------------------------------------------
1264 
1265 void
1266 plD_init_wingdi( PLStream *pls )
1267 {
1268  struct wingdi_Dev *dev;
1269  int status_bar = 0; // Default to no status bar
1270  int full_viewer = 0; // Default to the minimal viewer
1271  DrvOpt wingdi_options[] = {
1272  { "full", DRV_INT, &full_viewer, "Enable full function viewer (0|1)" },
1273  { "statusbar", DRV_INT, &status_bar, "Enable status bar (0|1)" },
1274  { NULL, DRV_INT, NULL, NULL }
1275  };
1276  TCHAR *program;
1277 #ifdef UNICODE
1278  int programlength;
1279 #endif
1280 
1281  pls->debug = 1;
1282  pldebug( "wingdi", "Device Init\n" );
1283 
1284  // Initialize the module
1285  wingdi_module_initialize();
1286 
1287  // Allocate and initialize device-specific data
1288  if ( pls->dev != NULL )
1289  free( (void *) pls->dev );
1290 
1291  pls->dev = calloc( 1, (size_t) sizeof ( struct wingdi_Dev ) );
1292  if ( pls->dev == NULL )
1293  plexit( "plD_init_wingdi_Dev: Out of memory." );
1294 
1295  // Shortcut to the device specific data structure
1296  dev = (struct wingdi_Dev *) pls->dev;
1297 
1298  pls->icol0 = 1; // Set a fall back pen color in case user doesn't
1299 
1300  pls->termin = 1; // interactive device
1301  pls->graphx = GRAPHICS_MODE; // No text mode for this driver
1302  pls->dev_fill0 = 1; // driver can do solid area fills
1303  pls->dev_xor = 1; // driver supports xor mode
1304  pls->dev_clear = 1; // driver supports clear
1305  pls->dev_text = 1; // driver supports text
1306  pls->dev_gradient = 0; // driver not support gradient fills
1307  pls->dev_dash = 0; // driver can not do dashed lines (yet)
1308  pls->plbuf_write = 1; // driver uses the buffer for redraws
1309 
1310  if ( !pls->colorset )
1311  pls->color = 1;
1312 
1313  // Check for and set up driver options
1314  plParseDrvOpts( wingdi_options );
1315 
1316  // Set the appropriate viewer type
1317  if ( full_viewer )
1318  dev->viewer = VIEWER_FULL;
1319  else
1320  dev->viewer = VIEWER_MINIMAL;
1321 
1322  // Determine which features should be enabled
1323  if ( status_bar )
1324  dev->feature.status_bar = 1;
1325  else
1326  dev->feature.status_bar = 0;
1327 
1328  // Set up the initial device parameters. This will be updated
1329  // after the plot window is initialized.
1330  if ( pls->xlength <= 0 || pls->ylength <= 0 )
1331  {
1332  // use default width, height of 800x600 if not specified by
1333  // -geometry option or plspage
1334  plspage( 0., 0., 800, 600, 0, 0 );
1335  }
1336 
1337  dev->width = pls->xlength - 1; // should I use -1 or not???
1338  dev->height = pls->ylength - 1;
1339 
1340 #ifdef UNICODE
1341  //convert the program name to wide char
1342  programlength = strlen( pls->program ) + 1;
1343  program = malloc( programlength * sizeof ( TCHAR ) );
1344  MultiByteToWideChar( CP_UTF8, 0, pls->program, programlength, program, programlength );
1345 #else
1346  program = pls->program;
1347 #endif
1348 
1349  if ( dev->viewer == VIEWER_FULL )
1350  {
1351  // Create our main window using the plot window class
1352  dev->frame = CreateWindowEx(
1353  WS_EX_WINDOWEDGE + WS_EX_LEFT,
1354  frame_window_class_name, // Class name
1355  program, // Caption
1356  WS_OVERLAPPEDWINDOW // Window style
1357  | WS_CLIPCHILDREN, // Exclude child area from parent
1358  pls->xoffset, // Initial x (use default)
1359  pls->yoffset, // Initial y (use default)
1360  pls->xlength, // Initial x size (use default)
1361  pls->ylength, // Initial y size (use default)
1362  NULL, // No parent window
1363  NULL, // No menu
1364  frame_window_class.hInstance, // This program instance
1365  NULL // Creation parameters
1366  );
1367 
1368  // Create the plot area
1369  dev->plot = CreateWindowEx(
1370  0,
1371  plot_area_class_name, // Class name
1372  NULL, // Caption
1373  WS_CHILD | WS_VISIBLE, // Style
1374  0, 0, 0, 0, // Position information
1375  dev->frame, // Parent window
1376  (HMENU) PlotAreaId, // No menu
1377  plot_area_class.hInstance, // This program instance
1378  NULL // Creation parameters
1379  );
1380  }
1381  else
1382  {
1383  // Create the plot area
1384  dev->plot = CreateWindowEx(
1385  WS_EX_WINDOWEDGE + WS_EX_LEFT,
1386  plot_area_class_name, // Class name
1387  NULL, // Caption
1388  WS_OVERLAPPEDWINDOW // Style
1389  | WS_VISIBLE,
1390  pls->xoffset, // Initial x (use default)
1391  pls->yoffset, // Initial y (use default)
1392  pls->xlength, // Initial x size (use default)
1393  pls->ylength, // Initial y size (use default)
1394  NULL, // Parent window
1395  NULL, // No menu
1396  plot_area_class.hInstance, // This program instance
1397  NULL // Creation parameters
1398  );
1399  }
1400 
1401  if ( dev->plot == NULL )
1402  {
1403  plexit( "wingdi: Failed to create plot area\n" );
1404  }
1405 
1406 #ifdef UNICODE
1407  free( program );
1408 #endif
1409 
1410  // Attach a pointer to the stream to the window's user area
1411  // this pointer will be used by the windows call back for
1412  // process this window
1413 #ifdef _WIN64
1414  SetWindowLongPtr( dev->plot, GWLP_USERDATA, (LONG_PTR) pls );
1415  if ( dev->frame )
1416  SetWindowLongPtr( dev->frame, GWLP_USERDATA, (LONG_PTR) dev );
1417 #else
1418  SetWindowLongPtr( dev->plot, GWL_USERDATA, (LONG) pls );
1419  if ( dev->frame )
1420  SetWindowLongPtr( dev->frame, GWL_USERDATA, (LONG) dev );
1421 #endif
1422 
1423  // Get the device context of the window that was created
1424  dev->hdc = GetDC( dev->plot );
1425 
1426  // Set the initial color for the drawing pen
1427  plD_state_wingdi( pls, PLSTATE_COLOR0 );
1428 
1429  // Display the window which we just created (using the nShow
1430  // passed by the OS, which allows for start minimized and that
1431  // sort of thing).
1432  if ( dev->viewer == VIEWER_FULL )
1433  {
1434  ShowWindow( dev->frame, SW_SHOWDEFAULT );
1435  SetForegroundWindow( dev->frame );
1436  }
1437  else
1438  {
1439  ShowWindow( dev->plot, SW_SHOWDEFAULT );
1440  SetForegroundWindow( dev->plot );
1441  }
1442 
1443  if ( dev->feature.status_bar )
1444  {
1445  // Get the handle for the status bar. This will
1446  // make it easier to update the contents
1447  dev->status_bar = GetDlgItem( dev->frame, StatusBarId );
1448  }
1449 
1450  // Now we have to find out, from windows, just how big our drawing area is
1451  // when we specified the page size earlier on, that includes the borders,
1452  // title bar etc... so now that windows has done all its initialisations,
1453  // we will ask how big the drawing area is, and tell plplot
1454  UpdatePageMetrics( pls, DEV_WINDOW );
1455  plspage( pls->xdpi, pls->ydpi, 0, 0, 0, 0 );
1456 
1457  // Set fill rule.
1458  if ( pls->dev_eofill )
1459  SetPolyFillMode( dev->hdc, ALTERNATE );
1460  else
1461  SetPolyFillMode( dev->hdc, WINDING );
1462 
1463  // Erase the plot window
1464  Erase( pls );
1465 
1466  // Indicate that the plot window is active
1467  dev->state = DEV_ACTIVE;
1468  update_status_bar( dev );
1469 }
1470 
1471 //--------------------------------------------------------------------------
1472 // plD_line_wingdi()
1473 //
1474 // Draw a line in the current color from (x1,y1) to (x2,y2).
1475 //--------------------------------------------------------------------------
1476 
1477 static void
1478 plD_line_wingdi( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
1479 {
1480  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
1481  HGDIOBJ previous_obj;
1482  POINT points[2];
1483 
1484  points[0].x = (LONG) ( x1a / dev->xscale );
1485  points[1].x = (LONG) ( x2a / dev->xscale );
1486  points[0].y = (LONG) ( dev->height - ( y1a / dev->yscale ) );
1487  points[1].y = (LONG) ( dev->height - ( y2a / dev->yscale ) );
1488 
1489  previous_obj = SelectObject( dev->hdc, dev->pen );
1490 
1491  if ( points[0].x != points[1].x || points[0].y != points[1].y )
1492  {
1493  Polyline( dev->hdc, points, 2 );
1494  }
1495  else
1496  {
1497  SetPixel( dev->hdc, points[0].x, points[0].y, dev->color );
1498  }
1499  SelectObject( dev->hdc, previous_obj );
1500 }
1501 
1502 //--------------------------------------------------------------------------
1503 // plD_polyline_wingdi()
1504 //
1505 // Draw a polyline in the current color.
1506 //--------------------------------------------------------------------------
1507 
1508 static void
1509 plD_polyline_wingdi( PLStream *pls, short *xa, short *ya, PLINT npts )
1510 {
1511  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
1512  int i;
1513  POINT *points = NULL;
1514  HGDIOBJ previous_obj;
1515 
1516  if ( npts > 0 )
1517  {
1518  points = HeapAlloc( wingdi_heap, HEAP_ZERO_MEMORY,
1519  (size_t) npts * sizeof ( POINT ) );
1520  if ( points != NULL )
1521  {
1522  for ( i = 0; i < npts; i++ )
1523  {
1524  points[i].x = (LONG) ( xa[i] / dev->xscale );
1525  points[i].y = (LONG) ( dev->height - ( ya[i] / dev->yscale ) );
1526  }
1527 
1528  previous_obj = SelectObject( dev->hdc, dev->pen );
1529  Polyline( dev->hdc, points, npts );
1530  SelectObject( dev->hdc, previous_obj );
1531  HeapFree( wingdi_heap, 0, points );
1532  }
1533  else
1534  {
1535  plexit( "Could not allocate memory to \"plD_polyline_wingdi\"\n" );
1536  }
1537  }
1538 }
1539 
1540 //--------------------------------------------------------------------------
1541 // plD_fill_polygon_wingdi()
1542 //
1543 // Fill polygon described in points pls->dev_x[] and pls->dev_y[].
1544 //--------------------------------------------------------------------------
1545 static void
1546 plD_fill_polygon_wingdi( PLStream *pls )
1547 {
1548  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
1549  int i;
1550  POINT *points = NULL;
1551  HGDIOBJ previous_brush, previous_pen;
1552  HPEN hpen;
1553  HBRUSH fillbrush;
1554 
1555  // Do nothing if there are no points
1556  if ( pls->dev_npts == 0 )
1557  return;
1558 
1559  points = HeapAlloc( wingdi_heap, HEAP_ZERO_MEMORY,
1560  (size_t) pls->dev_npts * sizeof ( POINT ) );
1561 
1562  if ( points == NULL )
1563  plexit( "Could not allocate memory to \"plD_fill_polygon_wingdi\"\n" );
1564 
1565  for ( i = 0; i < pls->dev_npts; i++ )
1566  {
1567  points[i].x = (PLINT) ( pls->dev_x[i] / dev->xscale );
1568  points[i].y = (PLINT) ( dev->height - ( pls->dev_y[i] / dev->yscale ) );
1569  }
1570 
1571  // Create a brush for the fill and a pen for the border
1572  fillbrush = CreateSolidBrush( dev->color );
1573  hpen = CreatePen( PS_SOLID, 1, dev->color );
1574  previous_brush = SelectObject( dev->hdc, fillbrush );
1575  previous_pen = SelectObject( dev->hdc, hpen );
1576 
1577  // Draw the filled polygon
1578  Polygon( dev->hdc, points, pls->dev_npts );
1579 
1580  // Restore the previous objects and delete
1581  SelectObject( dev->hdc, previous_brush );
1582  DeleteObject( fillbrush );
1583  SelectObject( dev->hdc, previous_pen );
1584  DeleteObject( hpen );
1585 
1586  HeapFree( wingdi_heap, 0, points );
1587 }
1588 
1589 //--------------------------------------------------------------------------
1590 // plD_clear_wingdi()
1591 //
1592 // Clears the client area of a window.
1593 //--------------------------------------------------------------------------
1594 static void
1595 plD_clear_wingdi( PLStream *pls, PLINT x1, PLINT y1, PLINT x2, PLINT y2 )
1596 {
1597  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
1598  COLORREF previous_color;
1599  RECT rect;
1600 
1601  if ( x1 >= 0 && y1 >= 0 && x2 >= 0 && y2 >= 0 )
1602  {
1603  rect.left = (LONG) ( x1 / dev->xscale );
1604  rect.top = (LONG) ( dev->height - ( y1 / dev->yscale ) );
1605  rect.right = (LONG) ( x2 / dev->xscale );
1606  rect.bottom = (LONG) ( dev->height - ( y2 / dev->yscale ) );
1607  }
1608  else
1609  {
1610  // Invalid coordinates, erase the entire area
1611  GetClientRect( dev->plot, &rect );
1612  }
1613 
1614  // This is a new "High Speed" way of filling in the background.
1615  // supposedly this executes faster than creating a brush and
1616  // filling a rectangle - go figure ?
1617  previous_color = SetBkColor( dev->hdc,
1618  RGB( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b ) );
1619  ExtTextOut( dev->hdc, 0, 0, ETO_OPAQUE, &rect, _T( "" ), 0, 0 );
1620  SetBkColor( dev->hdc, previous_color );
1621 }
1622 
1623 struct _text_state
1624 {
1625  // Font information
1626  PLFLT font_height;
1627  LONG font_weight;
1628  BYTE font_charset;
1629  BYTE font_pitch;
1630  BYTE font_family;
1631  BYTE italic;
1632 
1633  // Font transformation
1634  PLFLT rotation, shear, stride;
1635 
1636  // Super/subscript state
1637  PLINT level; // super/subscript level
1638  PLFLT old_sscale, sscale;
1639  PLFLT old_soffset, soffset;
1640 };
1641 
1642 // set_font()
1643 //
1644 // Sets the current font of the plot window device context to one that
1645 // best matches the desired attributes. The previous font is returned so
1646 // that the caller can restore the original font. This routine caches the
1647 // fonts that it creates to improve performance.
1648 static HFONT
1649 set_font( struct wingdi_Dev * dev,
1650  LONG font_height, LONG escapement,
1651  LONG weight, BYTE italic,
1652  BYTE charset,
1653  BYTE pitch, BYTE family )
1654 {
1655  struct _font_entry *ptr = font_list;
1656 
1657  for ( ptr = font_list; ptr != NULL; ptr = ptr->next )
1658  {
1659  if (
1660  ptr->info.lfHeight == font_height
1661  && ptr->info.lfEscapement == escapement
1662  && ptr->info.lfWeight == weight
1663  && ptr->info.lfItalic == italic
1664  && ptr->info.lfCharSet == charset
1665  && ptr->info.lfPitchAndFamily == ( pitch | family )
1666  && ptr->hdc == dev->hdc
1667  )
1668  {
1669  return SelectObject( dev->hdc, ptr->font );
1670  }
1671  }
1672 
1673  // Allocate space for this font entry
1674  ptr = HeapAlloc( wingdi_heap, 0, sizeof ( struct _font_entry ) );
1675 
1676  // The font was not found, thus we need to create it
1677  ptr->info.lfHeight = font_height;
1678  ptr->info.lfWidth = 0;
1679  ptr->info.lfEscapement = escapement; // Escapement angle
1680  ptr->info.lfOrientation = 0; // Orientation angle
1681  ptr->info.lfWeight = weight; // Font weight (e.g. bold)
1682  ptr->info.lfItalic = italic;
1683  ptr->info.lfUnderline = 0;
1684  ptr->info.lfStrikeOut = 0;
1685  ptr->info.lfCharSet = charset;
1686  ptr->info.lfOutPrecision = OUT_OUTLINE_PRECIS;
1687  ptr->info.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1688  ptr->info.lfQuality = ANTIALIASED_QUALITY;
1689  ptr->info.lfPitchAndFamily = pitch | family;
1690  ptr->font = CreateFontIndirect( &( ptr->info ) );
1691  ptr->hdc = dev->hdc;
1692  ptr->next = NULL;
1693 
1694  if ( ptr->font == NULL )
1695  {
1696  plwarn( "wingdi: Unable to create a font, using default\n" );
1697  HeapFree( wingdi_heap, 0, ptr );
1698  return NULL;
1699  }
1700  else
1701  {
1702  if ( font_list != NULL )
1703  {
1704  ptr->next = font_list;
1705  font_list = ptr;
1706  }
1707  else
1708  {
1709  font_list = ptr;
1710  }
1711  }
1712 
1713  return SelectObject( dev->hdc, ptr->font );
1714 }
1715 
1716 // Find the corresponding ANSI value for a given
1717 // Hershey character
1718 static const char hershey_to_ansi_lookup[] =
1719 {
1720  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
1721  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
1722  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20
1723  0, 0, 0, '!', '"', '#', '$', '%', '&', '\'', // 30
1724  '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', // 40
1725  '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', // 50
1726  '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', // 60
1727  'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', // 70
1728  'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', // 80
1729  'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', // 90
1730  'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', // 100
1731  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', // 110
1732  'x', 'y', 'z', '{', '|', '}', '~', 0, 0, 0, // 120
1733 };
1734 static const char hershey_to_symbol_lookup[] =
1735 {
1736  0, 0xB7, 0x2B, 0x2A, 0, 0, 0, 0, 0, 0, // 0
1737  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
1738  0, 0, 0, 0, 0, 0, 0, 0, 0xAC, 0xAE, // 20
1739  0xAD, 0xAF
1740 };
1741 
1742 static void
1743 process_text_escape( struct wingdi_Dev *dev, EscText *args,
1744  int *i, char *buffer, int *j,
1745  struct _text_state *state )
1746 {
1747  int val;
1748 
1749  // Get the next character to determine what the escape action is
1750  switch ( args->string[++( *i )] )
1751  {
1752  case 'u': // Superscript or end of subscript
1753  plP_script_scale( TRUE, &state->level,
1754  &state->old_sscale, &state->sscale,
1755  &state->old_soffset, &state->soffset );
1756 
1757  set_font( dev,
1758  (LONG) ( state->font_height * state->sscale ),
1759  (LONG) ( state->rotation ),
1760  state->font_weight,
1761  state->italic,
1762  state->font_charset,
1763  state->font_pitch, state->font_family );
1764  break;
1765  case 'd': // Subscript or end of superscript
1766  plP_script_scale( FALSE, &state->level,
1767  &state->old_sscale, &state->sscale,
1768  &state->old_soffset, &state->soffset );
1769 
1770  set_font( dev,
1771  (LONG) ( state->font_height * state->sscale ),
1772  (LONG) ( state->rotation ),
1773  state->font_weight,
1774  state->italic,
1775  state->font_charset,
1776  state->font_pitch, state->font_family );
1777  break;
1778  case 'f': // Font switch
1779  switch ( args->string[++( *i )] )
1780  {
1781  case 'n':
1782  state->font_family = FF_SWISS;
1783  state->italic = 0;
1784  state->font_charset = ANSI_CHARSET;
1785  break;
1786  case 'r':
1787  state->font_family = FF_ROMAN;
1788  state->italic = 0;
1789  state->font_charset = ANSI_CHARSET;
1790  break;
1791  case 'i':
1792  state->font_family = FF_ROMAN;
1793  state->italic = 1;
1794  state->font_charset = ANSI_CHARSET;
1795  break;
1796  case 's':
1797  state->font_family = FF_SCRIPT;
1798  state->italic = 0;
1799  state->font_charset = ANSI_CHARSET;
1800  break;
1801  }
1802  set_font( dev,
1803  (LONG) ( state->font_height * state->sscale ),
1804  (LONG) ( state->rotation ),
1805  state->font_weight,
1806  state->italic,
1807  state->font_charset,
1808  state->font_pitch, state->font_family );
1809  break;
1810  case 'g': // Greek character font
1811  state->font_family = FF_DONTCARE;
1812  state->italic = 0;
1813  state->font_charset = GREEK_CHARSET;
1814 
1815  set_font( dev,
1816  (LONG) ( state->font_height * state->sscale ),
1817  (LONG) ( state->rotation ),
1818  state->font_weight,
1819  state->italic,
1820  state->font_charset,
1821  state->font_pitch, state->font_family );
1822  break;
1823  case '(': // Hershey character specified
1824  sscanf( args->string + *i, "%d", &val );
1825 
1826  if ( val > 0 && val < ARRAY_SIZE( hershey_to_ansi_lookup )
1827  && hershey_to_ansi_lookup[val] != 0 )
1828  {
1829  buffer[( *j )++] = hershey_to_ansi_lookup[val];
1830  }
1831  else
1832  {
1833  plwarn( "Unsupported hershey character\n" );
1834  // Substitute a bullet so something is displayed
1835  buffer[( *j )++] = 0x95;
1836  }
1837  *i += 4;
1838  break;
1839  case '[': // Unicode character specified
1840  plwarn( "Unicode characters are not supported\n" );
1841  *i += 4;
1842  break;
1843  default:
1844  plwarn( "Ignoring escape code %c\n" );
1845  //plwarn( "Ignoring escape code %c\n", args->string[i-1] );
1846  }
1847 }
1848 
1849 //--------------------------------------------------------------------------
1850 // plD_text_wingdi()
1851 //
1852 // Renders text on the string. Support non-Unicode and Unicode strings.
1853 //--------------------------------------------------------------------------
1854 static void
1855 plD_text_wingdi( PLStream * pls, EscText * args )
1856 {
1857  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
1858  struct _text_state state;
1859  PLFLT cos_rot, sin_rot;
1860  PLFLT cos_shear, sin_shear;
1861  int rx, ry;
1862  UINT halign, valign;
1863  HFONT font = NULL, prev_font = NULL;
1864  COLORREF prev_color;
1865  int prev_bkmode;
1866  int text_segments;
1867  char esc;
1868 
1869  // Get the escape character used to format strings
1870  plgesc( &esc );
1871 
1872  // Determine the font characteristics
1873  state.italic = 0;
1874  state.font_weight = FW_NORMAL;
1875  state.font_charset = ANSI_CHARSET;
1876  state.font_pitch = DEFAULT_PITCH;
1877  state.font_family = FF_DONTCARE;
1878  switch ( pls->cfont )
1879  {
1880  case 1:
1881  // normal = (medium, upright, sans serif)
1882  state.font_family = FF_SWISS;
1883  break;
1884  case 2:
1885  // roman = (medium, upright, serif)
1886  state.font_family = FF_ROMAN;
1887  break;
1888  case 3:
1889  // italic = (medium, italic, serif)
1890  state.font_family = FF_ROMAN;
1891  state.italic = 1;
1892  break;
1893  case 4:
1894  // script = (medium, upright, script)
1895  state.font_family = FF_SCRIPT;
1896  break;
1897  }
1898 
1899  // Calculate the font size from mm to device units
1900  state.font_height = 1.6 * pls->chrht * dev->ydpmm;
1901 
1902  plRotationShear( args->xform,
1903  &state.rotation, &state.shear, &state.stride );
1904  state.rotation -= pls->diorot * PI / 2.0;
1905  cos_rot = (PLFLT) cos( state.rotation );
1906  sin_rot = (PLFLT) sin( state.rotation );
1907  cos_shear = (PLFLT) cos( state.shear );
1908  sin_shear = (PLFLT) sin( state.shear );
1909  // Convert from radians to tenths of a degree, which is what
1910  // CreateFont() uses
1911  state.rotation = state.rotation * ( 180.0 / M_PI ) * 10.0;
1912 
1913  // Is the page rotated?
1914  if ( pls->diorot != 0 )
1915  {
1916  // The page is rotated. We need to apply the page rotation because
1917  // args x,y are not rotated.
1918  PLFLT x, y, ox, oy;
1919  PLFLT cos_theta, sin_theta;
1920 
1921  // Translate origin of the virtual coordinates to the midpoint
1922  ox = (PLFLT) args->x - PIXELS_X / 2;
1923  oy = (PLFLT) args->y - PIXELS_Y / 2;
1924 
1925  // Apply the orientation rotation
1926  cos_theta = cos( -pls->diorot * PI / 2.0 );
1927  sin_theta = sin( -pls->diorot * PI / 2.0 );
1928  x = cos_theta * (PLFLT) ox - sin_theta * (PLFLT) oy;
1929  y = sin_theta * (PLFLT) ox + cos_theta * (PLFLT) oy;
1930 
1931  // Untranslate origin
1932  x = x + PIXELS_X / 2;
1933  y = y + PIXELS_Y / 2;
1934 
1935  // Convert to device coordinates
1936  rx = (int) ( x / dev->xscale );
1937  ry = (int) ( dev->height - ( y / dev->yscale ) );
1938  }
1939  else
1940  {
1941  rx = (int) ( args->x / dev->xscale );
1942  ry = (int) ( dev->height - ( args->y / dev->yscale ) );
1943  }
1944 
1945  // Determine the location of the bounding rectangle relative to the
1946  // reference point (bottom, top, or center)
1947  // Windows draws the text within a rectangle, thus the rectangle
1948  // needs to be positioned relative to specified coordinate
1949  if ( args->base == 2 )
1950  {
1951  // If base = 2, it is aligned with the top of the text box
1952  valign = TA_TOP;
1953  }
1954  else if ( args->base == 1 )
1955  {
1956  // If base = 1, it is aligned with the baseline of the text box
1957  valign = TA_BOTTOM;
1958  }
1959  else
1960  {
1961  // If base = 0, it is aligned with the center of the text box
1962  valign = TA_TOP;
1963 
1964  // Adjust the reference point by 1/2 of the character height
1965  ry -= (int) ( 0.5 * pls->chrht * dev->ydpmm );
1966  }
1967 
1968  // Text justification. Left (0.0), center (0.5) and right (1.0)
1969  // justification, which are the more common options, are supported.
1970  if ( args->just < 0.499 )
1971  {
1972  // Looks left aligned
1973  halign = TA_LEFT;
1974  }
1975  else if ( args->just > 0.501 )
1976  {
1977  // Looks right aligned
1978  halign = TA_RIGHT;
1979  }
1980  else
1981  {
1982  halign = TA_CENTER;
1983  }
1984 
1985  prev_font = set_font( dev,
1986  (LONG) state.font_height,
1987  (LONG) state.rotation,
1988  state.font_weight,
1989  state.italic,
1990  state.font_charset,
1991  state.font_pitch,
1992  state.font_family );
1993  prev_color = SetTextColor( dev->hdc, dev->color );
1994  prev_bkmode = SetBkMode( dev->hdc, TRANSPARENT );
1995 
1996  if ( args->unicode_array_len == 0 )
1997  {
1998  // Non unicode string
1999  size_t i, j, len;
2000  char * buffer;
2001  int height = 0, width = 0;
2002 
2003  // Determine the string length and allocate a buffer to
2004  // use as a working copy of the output string. The passed
2005  // string will always be larger than the formatted version
2006  len = strlen( args->string );
2007  buffer = HeapAlloc( wingdi_heap, 0, ( len + 1 ) * sizeof ( char ) );
2008  if ( buffer == NULL )
2009  {
2010  plexit( "wingdi: Unable to allocate character buffer\n" );
2011  }
2012 
2013  // First we need to determine the number of text segments in the
2014  // string. Text segments are delimited by the escape characters
2015  text_segments = 1;
2016  state.level = 0;
2017  state.sscale = 1.0;
2018  for ( i = j = 0; i < len + 1; i++ )
2019  {
2020  if ( args->string[i] != esc && args->string[i] != '\0' )
2021  {
2022  // Copy characters into the buffer to build the
2023  // string segment
2024  buffer[j++] = args->string[i];
2025  }
2026  else if ( i != len && args->string[i + 1] == esc )
2027  {
2028  // We have two escape characters in a row, which means
2029  // to ignore the escape and copy the character
2030  buffer[j++] = args->string[i];
2031  i++;
2032  }
2033  else
2034  {
2035  if ( j > 0 )
2036  {
2037  SIZE segment_size;
2038 
2039  // NUL terminate what we have copied so far
2040  buffer[j] = '\0';
2041 
2042  // Determine the size of the text segment and add
2043  // it to the width of the overall string
2044  GetTextExtentPoint32( dev->hdc,
2045  buffer, j,
2046  &segment_size );
2047 
2048  // The effect of super/subscripts on the size of the
2049  // bounding box is ignored at this time. This will result
2050  // in small positional errors.
2051 
2052  width += segment_size.cx;
2053  if ( segment_size.cy > height )
2054  height = segment_size.cy;
2055  }
2056  j = 0;
2057  if ( i != len )
2058  {
2059  text_segments++;
2060  process_text_escape( dev, args,
2061  &i, buffer, &j,
2062  &state );
2063  }
2064  }
2065  }
2066 
2067  // Output the text segments
2068  if ( text_segments > 1 )
2069  {
2070  UINT save_text_align;
2071 
2072  // We have the total width of all the text segments. Determine
2073  // the initial reference point based on the desired alignment.
2074  // When rendering text, we use left alignment and adjust the reference
2075  // point accordingly.
2076  switch ( halign )
2077  {
2078  case TA_LEFT:
2079  break;
2080  case TA_RIGHT:
2081  rx -= width;
2082  break;
2083  case TA_CENTER:
2084  rx -= (int) ( cos_rot * 0.5 * (PLFLT) width );
2085  ry += (int) ( sin_rot * 0.5 * (PLFLT) width );
2086  break;
2087  }
2088  save_text_align = SetTextAlign( dev->hdc, TA_LEFT | valign );
2089 
2090  state.level = 0;
2091  state.sscale = 1.0;
2092  for ( i = j = 0; i < len + 1; i++ )
2093  {
2094  if ( args->string[i] != esc && args->string[i] != '\0' )
2095  {
2096  // Copy characters into the buffer to build the
2097  // string segment
2098  buffer[j++] = args->string[i];
2099  }
2100  else if ( i != len && args->string[i + 1] == esc )
2101  {
2102  // We have two escape characters in a row, which means
2103  // to ignore the escape and copy the character
2104  buffer[j++] = args->string[i];
2105  i++;
2106  }
2107  else
2108  {
2109  SIZE segment_size;
2110  int sx, sy;
2111 
2112  if ( j > 0 )
2113  {
2114  // NUL terminate what we have copied so far
2115  buffer[j] = '\0';
2116 
2117  GetTextExtentPoint32( dev->hdc,
2118  buffer, j,
2119  &segment_size );
2120 
2121  // Determine the offset due to super/subscripts
2122  sx = (int) ( ( 1.0 - cos_rot ) * (PLFLT) state.soffset );
2123  sy = (int) ( ( 1.0 - sin_rot ) * (PLFLT) state.soffset );
2124 
2125  TextOut( dev->hdc,
2126  rx + sx,
2127  ry + sy,
2128  buffer, j );
2129 
2130  rx += (int) ( cos_rot * (PLFLT) segment_size.cx );
2131  ry -= (int) ( sin_rot * (PLFLT) segment_size.cx );
2132  }
2133  j = 0;
2134 
2135  if ( i != len )
2136  process_text_escape( dev, args,
2137  &i, buffer, &j,
2138  &state );
2139  }
2140  }
2141  }
2142  HeapFree( wingdi_heap, 0, buffer );
2143  }
2144  else
2145  {
2146  // Unicode string
2147  }
2148 
2149  if ( text_segments == 1 )
2150  {
2151  // Only one text segment, thus this is a simple string
2152  // that can be written to the device in one step. We
2153  // check for this because many strings are simple and
2154  // this approach is faster
2155  UINT save_text_align;
2156 
2157  save_text_align = SetTextAlign( dev->hdc, halign | valign );
2158 
2159  TextOut( dev->hdc,
2160  rx, ry,
2161  args->string, strlen( args->string ) );
2162 
2163  SetTextAlign( dev->hdc, save_text_align );
2164  }
2165 
2166  // Restore the previous text settings
2167  if ( prev_font != NULL )
2168  SelectObject( dev->hdc, prev_font );
2169  SetTextColor( dev->hdc, prev_color );
2170  SetBkMode( dev->hdc, prev_bkmode );
2171 }
2172 
2173 //--------------------------------------------------------------------------
2174 // End of the page
2175 //--------------------------------------------------------------------------
2176 static void
2177 plD_eop_wingdi( PLStream *pls )
2178 {
2179  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
2180 
2181  pldebug( "wingdi", "End of the page\n" );
2182 
2183  if ( dev->type == DEV_WINDOW )
2184  // Save a bitmap of the current screen to help with redraws
2185  CopySCRtoBMP( pls );
2186  else
2187  EndPage( dev->hdc );
2188 
2189  // Set the cursor to normal to indicate that the window is not busy
2190  NormalCursor( dev );
2191 
2192  // Indicate that the driver is no longer drawing and is ready to continue
2193  dev->state = DEV_ACTIVE;
2194 }
2195 
2196 //--------------------------------------------------------------------------
2197 // Beginning of the new page
2198 //--------------------------------------------------------------------------
2199 static void
2200 plD_bop_wingdi( PLStream *pls )
2201 {
2202  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
2203  pldebug( "wingdi", "Start of Page\n" );
2204 
2205  // Indicate that the device is actively drawing
2206  dev->state = DEV_DRAWING;
2207 
2208  // Update the status bar
2209  update_status_bar( dev );
2210 
2211  // Change the cursor to indicate the program is busy
2212  BusyCursor( dev );
2213 
2214  if ( dev->type == DEV_WINDOW )
2215  {
2216  // Invalidate the page so that it gets erased on the WM_PAINT. It might be
2217  // better to just clear now and not invalidate.
2218  // NOTE: Is RedrawWindow needed only for the window output device type?
2219  //RedrawWindow( dev->plot, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW );
2220  SendMessage( dev->plot,
2221  WM_ERASEBKGND,
2222  (WPARAM) 0,
2223  (LPARAM) NULL );
2224  }
2225  else
2226  {
2227  StartPage( dev->hdc );
2228  }
2229 
2230  // Reset the pen color
2231  plD_state_wingdi( pls, PLSTATE_COLOR0 );
2232 }
2233 
2234 //--------------------------------------------------------------------------
2235 // Stream cleanup function
2236 //--------------------------------------------------------------------------
2237 static void
2238 plD_tidy_wingdi( PLStream *pls )
2239 {
2240  struct wingdi_Dev *dev = NULL;
2241 
2242  pldebug( "wingdi", "plD_tidy_wingdi\n" );
2243 
2244  if ( pls->dev != NULL )
2245  {
2246  dev = (struct wingdi_Dev *) pls->dev;
2247 
2248  if ( dev->hdc != NULL )
2249  ReleaseDC( dev->plot, dev->hdc );
2250 
2251  if ( dev->bitmap != NULL )
2252  DeleteObject( dev->bitmap );
2253 
2254  if ( dev->plot != NULL )
2255  DestroyWindow( dev->plot );
2256  if ( dev->status_bar != NULL )
2257  DestroyWindow( dev->status_bar );
2258  if ( dev->frame != NULL )
2259  DestroyWindow( dev->frame );
2260 
2261  free_mem( pls->dev );
2262 
2263  wingdi_module_cleanup();
2264  }
2265 }
2266 
2267 //--------------------------------------------------------------------------
2268 // plD_wait_wingdi()
2269 //
2270 // Wait for user input.
2271 //--------------------------------------------------------------------------
2272 
2273 static void
2274 plD_wait_wingdi( PLStream *pls )
2275 {
2276  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
2277 
2278  // Wait for the user to indicate the next action
2279  wait_for_user_input( pls );
2280 }
2281 
2282 //--------------------------------------------------------------------------
2283 // plD_state_wingdi()
2284 //
2285 // Handle change in PLStream state (color, pen width, fill attribute, etc).
2286 //--------------------------------------------------------------------------
2287 
2288 static void
2289 plD_state_wingdi( PLStream *pls, PLINT op )
2290 {
2291  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
2292 
2293  switch ( op )
2294  {
2295  case PLSTATE_COLOR0:
2296  case PLSTATE_COLOR1:
2297  dev->color = RGB( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b );
2298  break;
2299 
2300  case PLSTATE_CMAP0:
2301  case PLSTATE_CMAP1:
2302  dev->color = RGB( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b );
2303  break;
2304  }
2305 
2306  if ( dev->pen != NULL )
2307  DeleteObject( dev->pen );
2308  dev->pen = CreatePen( PS_SOLID, (int) pls->width, dev->color );
2309 }
2310 
2311 //--------------------------------------------------------------------------
2312 // plD_esc_wingdi()
2313 //
2314 // Handle PLplot escapes
2315 //--------------------------------------------------------------------------
2316 
2317 #ifndef PLESC_TELLME
2318 //
2319 // Placeholder code from Greg Jung until it gets incorporated
2320 // somewhere else
2321 //
2322 struct passmeup
2323 {
2324  MSG *msg;
2325  HWND *hwnd;
2326  HDC *hdc;
2327  void *roomformore[6];
2328  char *flags;
2329 };
2330 #define PLESC_TELLME 41
2331 #endif
2332 
2333 static void
2334 plD_esc_wingdi( PLStream *pls, PLINT op, void *ptr )
2335 {
2336  struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
2337 
2338  switch ( op )
2339  {
2340  case PLESC_GETC:
2341  GetCursorCmd( pls, (PLGraphicsIn *) ptr );
2342  break;
2343 
2344  case PLESC_FILL:
2345  plD_fill_polygon_wingdi( pls );
2346  break;
2347 
2348  case PLESC_CLEAR:
2349  plD_clear_wingdi( pls,
2350  pls->sppxmi, pls->sppymi,
2351  pls->sppxma, pls->sppyma );
2352  break;
2353 
2354  case PLESC_HAS_TEXT:
2355  plD_text_wingdi( pls, (EscText *) ptr );
2356  break;
2357 
2358  case PLESC_DOUBLEBUFFERING:
2359  // Ignore double buffering requests. It causes problems with
2360  // strip charts and most machines are fast enough that it
2361  // does not make much of a difference.
2362  break;
2363 
2364  case PLESC_XORMOD:
2365  if ( *(PLINT *) ( ptr ) == 0 )
2366  SetROP2( dev->hdc, R2_COPYPEN );
2367  else
2368  SetROP2( dev->hdc, R2_XORPEN );
2369  break;
2370 
2371  case PLESC_START_RASTERIZE:
2372  //
2373 // pldebug( "wingdi", "Start Rasterize\n" );
2374 // // The image rasterization in PLplot uses fills. It indicates
2375 // // the start and end with a PLESC_START_RASTERIZE and a
2376 // // PLESC_END_RASTERIZE escape code. Thus, when these codes are
2377 // // received, the wingdi driver will switch from rendering to the
2378 // // display and render to a bitmap instead
2379 //
2380 // // Save the original plot area DC
2381 // dev->save_hdc = dev->hdc;
2382 //
2383 // // Create a new DC and a bitmap
2384 // dev->hdc = CreateCompatibleDC( dev->save_hdc );
2385 // GetClientRect( dev->plot, &dev->raster_rect );
2386 // dev->raster_bmp = CreateCompatibleBitmap( dev->hdc,
2387 // dev->raster_rect.right,
2388 // dev->raster_rect.bottom );
2389 // SelectObject( dev->hdc, dev->raster_bmp );
2390  //
2391  break;
2392 
2393  case PLESC_END_RASTERIZE:
2394  //
2395 // pldebug( "wingdi", "Stop Rasterize\n" );
2396 // // Display the bitmap
2397 // BitBlt( dev->save_hdc,
2398 // dev->raster_rect.left, dev->raster_rect.top,
2399 // dev->raster_rect.right, dev->raster_rect.bottom,
2400 // dev->hdc,
2401 // dev->raster_rect.left, dev->raster_rect.top,
2402 // SRCCOPY );
2403 //
2404 // // Cleanup
2405 // if ( dev->raster_bmp != NULL )
2406 // DeleteObject( dev->raster_bmp );
2407 // if ( dev->hdc != NULL )
2408 // DeleteDC( dev->hdc );
2409 //
2410 // // Restore the original DC
2411 // dev->hdc = dev->save_hdc;
2412  //
2413  break;
2414 
2415  case PLESC_TELLME:
2416  {
2417  struct passmeup *pup = ptr;
2418 
2419  pup->hwnd = &( dev->plot );
2420  pup->hdc = &( dev->hdc );
2421  }
2422  break;
2423  }
2424 }
2425 
2426 #else
2427 int
2429 {
2430  return ( 0 );
2431 }
2432 
2433 #endif // PLD_wingdidev
int plParseDrvOpts(DrvOpt *acc_opt)
Definition: plargs.c:1461
void plP_script_scale(PLBOOL ifupper, PLINT *level, PLFLT *old_scale, PLFLT *scale, PLFLT *old_offset, PLFLT *offset)
Definition: plsym.c:1302
#define PLSTATE_CMAP0
Definition: plplotP.h:366
void plgesc(char *p_esc)
Definition: plcore.c:3914
#define PLESC_XORMOD
Definition: plplot.h:286
void plexit(PLCHAR_VECTOR errormsg)
Definition: plctrl.c:1958
#define plspage
Definition: plplot.h:831
PLFLT just
Definition: plplotP.h:708
unsigned char b
Definition: plplot.h:550
PLFLT dX
Definition: plplot.h:442
#define PLESC_FILL
Definition: plplot.h:279
PLINT sppxma
Definition: plstrm.h:703
#define PLESC_DOUBLEBUFFERING
Definition: plplot.h:285
plD_esc_fp pl_esc
Definition: disptab.h:90
void(* plD_tidy_fp)(struct PLStream_struct *)
Definition: disptab.h:72
PLINT dev_text
Definition: plstrm.h:572
PLFLT xdpi
Definition: plstrm.h:616
void plGinInit(PLGraphicsIn *gin)
Definition: plctrl.c:2887
PLINT cfont
Definition: plstrm.h:737
PLINT dev_npts
Definition: plstrm.h:581
const char * pl_MenuStr
Definition: disptab.h:79
PLINT color
Definition: plstrm.h:569
plD_tidy_fp pl_tidy
Definition: disptab.h:88
PLINT plbuf_write
Definition: plstrm.h:567
void(* plD_init_fp)(struct PLStream_struct *)
Definition: disptab.h:67
PLINT dev_clear
Definition: plstrm.h:572
PLFLT diorot
Definition: plstrm.h:661
void(* plD_eop_fp)(struct PLStream_struct *)
Definition: disptab.h:70
void(* plD_wait_fp)(struct PLStream_struct *)
Definition: disptab.h:75
const char * pl_DevName
Definition: disptab.h:80
#define PLSTATE_COLOR0
Definition: plplotP.h:363
plD_init_fp pl_init
Definition: disptab.h:83
PLINT colorset
Definition: plstrm.h:569
char * program
Definition: plstrm.h:530
#define PLSTATE_COLOR1
Definition: plplotP.h:364
PLINT sppxmi
Definition: plstrm.h:703
short * dev_x
Definition: plstrm.h:582
static int color
Definition: ps.c:78
int PLINT
Definition: plplot.h:181
void(* plD_line_fp)(struct PLStream_struct *, short, short, short, short)
Definition: disptab.h:68
PLINT sppyma
Definition: plstrm.h:703
unsigned char g
Definition: plplot.h:549
void(* plD_esc_fp)(struct PLStream_struct *, PLINT, void *)
Definition: disptab.h:74
void(* plD_polyline_fp)(struct PLStream_struct *, short *, short *, PLINT)
Definition: disptab.h:69
#define snprintf
Definition: plplotP.h:235
PLINT termin
Definition: plstrm.h:568
#define PLSTATE_CMAP1
Definition: plplotP.h:367
PLINT icol0
Definition: plstrm.h:539
#define PIXELS_X
Definition: plplotP.h:304
#define TRUE
Definition: plplotP.h:176
PLINT ylength
Definition: plstrm.h:617
#define FALSE
Definition: plplotP.h:177
plD_bop_fp pl_bop
Definition: disptab.h:87
int pldummy_wingdi()
Definition: wingdi.c:2428
#define PLESC_END_RASTERIZE
Definition: plplot.h:303
#define PLESC_CLEAR
Definition: plplot.h:288
PLINT dev_gradient
Definition: plstrm.h:773
plD_line_fp pl_line
Definition: disptab.h:84
static PLINT * buffer
Definition: plfill.c:74
static PLCHAR_VECTOR program
Definition: plargs.c:178
void plP_setpxl(PLFLT xpmm, PLFLT ypmm)
Definition: plcore.c:4238
PLColor * cmap0
Definition: plstrm.h:544
#define PLDLLIMPEXP_DRIVER
Definition: pldll.h:81
PLINT dev_xor
Definition: plstrm.h:572
PLFLT ydpi
Definition: plstrm.h:616
unsigned int keysym
Definition: plplot.h:437
static PLStream * pls[PL_NSTREAMS]
Definition: plcore.h:88
void plP_setphy(PLINT xmin, PLINT xmax, PLINT ymin, PLINT ymax)
Definition: plcore.c:4249
PLINT xlength
Definition: plstrm.h:617
void plRotationShear(PLFLT *xFormMatrix, PLFLT *rotation, PLFLT *shear, PLFLT *stride)
Definition: plot3d.c:2767
PLINT yoffset
Definition: plstrm.h:618
unsigned short unicode_array_len
Definition: plplotP.h:736
short * dev_y
Definition: plstrm.h:582
#define PIXELS_Y
Definition: plplotP.h:305
plD_wait_fp pl_wait
Definition: disptab.h:91
void plRemakePlot(PLStream *pls)
Definition: plbuf.c:1397
float PLFLT
Definition: plplot.h:163
PLINT graphx
Definition: plstrm.h:568
#define PLESC_START_RASTERIZE
Definition: plplot.h:302
PLFLT chrht
Definition: plstrm.h:686
void(* plD_bop_fp)(struct PLStream_struct *)
Definition: disptab.h:71
#define free_mem(a)
Definition: plplotP.h:182
#define PI
Definition: plplotP.h:290
PLDLLIMPEXP_DRIVER void plD_dispatch_init_wingdi(PLDispatchTable *pdt)
unsigned int state
Definition: plplot.h:436
unsigned char r
Definition: plplot.h:548
#define PLESC_HAS_TEXT
Definition: plplot.h:290
PLFLT width
Definition: plstrm.h:552
void plwarn(PLCHAR_VECTOR errormsg)
Definition: plctrl.c:1863
PLINT y
Definition: plplotP.h:713
PLINT dev_dash
Definition: plstrm.h:571
unsigned int button
Definition: plplot.h:438
PLColor curcolor
Definition: plstrm.h:543
plD_state_fp pl_state
Definition: disptab.h:89
PLFLT dY
Definition: plplot.h:442
const char * string
Definition: plplotP.h:739
plD_eop_fp pl_eop
Definition: disptab.h:86
PLINT xoffset
Definition: plstrm.h:618
PLINT debug
Definition: plstrm.h:527
PLINT x
Definition: plplotP.h:712
#define PLESC_GETC
Definition: plplot.h:283
plD_polyline_fp pl_polyline
Definition: disptab.h:85
void(* plD_state_fp)(struct PLStream_struct *, PLINT)
Definition: disptab.h:73
void * dev
Definition: plstrm.h:594
#define M_PI
Definition: plplotP.h:119
PLINT sppymi
Definition: plstrm.h:703
PLINT dev_fill0
Definition: plstrm.h:571
PLFLT * xform
Definition: plplotP.h:709
PLINT dev_eofill
Definition: plstrm.h:788
PLINT base
Definition: plplotP.h:707
#define GRAPHICS_MODE
Definition: plplotP.h:288