import -ko -b 1.1.3 XEmacs XEmacs-21_2 r21-2-35
[chise/xemacs-chise.git.1] / src / device-msw.c
1 /* device functions for mswindows.
2    Copyright (C) 1994, 1995 Board of Trustees, University of Illinois.
3    Copyright (C) 1994, 1995 Free Software Foundation, Inc.
4
5 This file is part of XEmacs.
6
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
11
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING.  If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21
22 /* Synched up with: Not in FSF. */
23
24 /* Authorship:
25
26    Original authors: Jamie Zawinski and the FSF
27    Rewritten by Ben Wing and Chuck Thompson.
28    Rewritten for mswindows by Jonathan Harris, November 1997 for 21.0.
29 */
30
31
32 #include <config.h>
33 #include "lisp.h"
34
35 #include "console-msw.h"
36 #include "console-stream.h"
37 #include "objects-msw.h"
38 #include "events.h"
39 #include "faces.h"
40 #include "frame.h"
41 #include "sysdep.h"
42
43 /* #### Andy, these includes might break cygwin compilation - kkm*/
44 #include <commdlg.h>
45 #include <winspool.h>
46
47 #if !(defined (CYGWIN) || defined(MINGW))
48 # include <objbase.h>   /* For CoInitialize */
49 #endif
50
51 /* win32 DDE management library globals */
52 #ifdef HAVE_DRAGNDROP
53 DWORD mswindows_dde_mlid;
54 HSZ mswindows_dde_service;
55 HSZ mswindows_dde_topic_system;
56 HSZ mswindows_dde_item_open;
57 #endif
58
59 /* Control conversion of upper case file names to lower case.
60    nil means no, t means yes. */
61 Lisp_Object Vmswindows_downcase_file_names;
62
63 /* Control whether stat() attempts to determine file type and link count
64    exactly, at the expense of slower operation.  Since true hard links
65    are supported on NTFS volumes, this is only relevant on NT.  */
66 Lisp_Object Vmswindows_get_true_file_attributes;
67
68 Lisp_Object Qinit_pre_mswindows_win, Qinit_post_mswindows_win;
69 Lisp_Object Qdevmodep;
70
71 static Lisp_Object allocate_devmode (DEVMODE* src_devmode, int do_copy,
72                                      char* src_name, struct device *d);
73
74 /************************************************************************/
75 /*                               helpers                                */
76 /************************************************************************/
77
78 static Lisp_Object
79 build_syscolor_string (int idx)
80 {
81   return (idx < 0 ? Qnil : mswindows_color_to_string (GetSysColor (idx)));
82 }
83
84 static Lisp_Object
85 build_syscolor_cons (int index1, int index2)
86 {
87   Lisp_Object color1, color2;
88   struct gcpro gcpro1;
89   GCPRO1 (color1);
90   color1 = build_syscolor_string (index1);
91   color2 = build_syscolor_string (index2);
92   RETURN_UNGCPRO (Fcons (color1, color2));
93 }
94
95 static Lisp_Object
96 build_sysmetrics_cons (int index1, int index2)
97 {
98   return Fcons (index1 < 0 ? Qnil : make_int (GetSystemMetrics (index1)),
99                 index2 < 0 ? Qnil : make_int (GetSystemMetrics (index2)));
100 }
101
102 static Lisp_Object
103 build_devicecaps_cons (HDC hdc, int index1, int index2)
104 {
105   return Fcons (index1 < 0 ? Qnil : make_int (GetDeviceCaps (hdc, index1)),
106                 index2 < 0 ? Qnil : make_int (GetDeviceCaps (hdc, index2)));
107 }
108
109 \f
110 /************************************************************************/
111 /*                          display methods                             */
112 /************************************************************************/
113
114 static void
115 mswindows_init_device (struct device *d, Lisp_Object props)
116 {
117   WNDCLASSEX wc;
118   HDC hdc;
119
120   DEVICE_CLASS (d) = Qcolor;
121   DEVICE_INFD (d) = DEVICE_OUTFD (d) = -1;
122   init_baud_rate (d);
123   init_one_device (d);
124
125   d->device_data = xnew_and_zero (struct mswindows_device);
126   hdc = CreateCompatibleDC (NULL);
127   assert (hdc!=NULL);
128   DEVICE_MSWINDOWS_HCDC(d) = hdc;
129   DEVICE_MSWINDOWS_FONTLIST (d) = mswindows_enumerate_fonts (hdc);
130   DEVICE_MSWINDOWS_UPDATE_TICK (d) = GetTickCount ();
131
132   /* Register the main window class */
133   wc.cbSize = sizeof (WNDCLASSEX);
134   wc.style = CS_OWNDC;  /* One DC per window */
135   wc.lpfnWndProc = (WNDPROC) mswindows_wnd_proc;
136   wc.cbClsExtra = 0;
137   wc.cbWndExtra = MSWINDOWS_WINDOW_EXTRA_BYTES;
138   /* This must match whatever is passed to CreateWIndowEx, NULL is ok
139      for this. */
140   wc.hInstance = NULL;  
141   wc.hIcon = LoadIcon (GetModuleHandle(NULL), XEMACS_CLASS);
142   wc.hCursor = LoadCursor (NULL, IDC_ARROW);
143   /* Background brush is only used during sizing, when XEmacs cannot
144      take over */
145   wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
146   wc.lpszMenuName = NULL;
147
148   wc.lpszClassName = XEMACS_CLASS;
149   wc.hIconSm = (HICON) LoadImage (GetModuleHandle (NULL), XEMACS_CLASS,
150                           IMAGE_ICON, 16, 16, 0);
151   RegisterClassEx (&wc);
152
153 #ifdef HAVE_WIDGETS
154   xzero (wc);
155   /* Register the main window class */
156   wc.cbSize = sizeof (WNDCLASSEX);
157   wc.lpfnWndProc = (WNDPROC) mswindows_control_wnd_proc;
158   wc.lpszClassName = XEMACS_CONTROL_CLASS;
159   wc.hInstance = NULL;
160   RegisterClassEx (&wc);
161 #endif
162
163 #if defined (HAVE_TOOLBARS) || defined (HAVE_WIDGETS)
164   InitCommonControls ();
165 #endif
166 }
167
168 static void
169 mswindows_finish_init_device (struct device *d, Lisp_Object props)
170 {
171   /* Initialize DDE management library and our related globals. We execute a
172    * dde Open("file") by simulating a drop, so this depends on dnd support. */
173 #ifdef HAVE_DRAGNDROP
174 # if !(defined(CYGWIN) || defined(MINGW))
175   CoInitialize (NULL);
176 # endif
177
178   mswindows_dde_mlid = 0;
179   DdeInitialize (&mswindows_dde_mlid, (PFNCALLBACK)mswindows_dde_callback,
180                  APPCMD_FILTERINITS|CBF_FAIL_SELFCONNECTIONS|CBF_FAIL_ADVISES|
181                  CBF_FAIL_POKES|CBF_FAIL_REQUESTS|CBF_SKIP_ALLNOTIFICATIONS,
182                  0);
183   
184   mswindows_dde_service = DdeCreateStringHandle (mswindows_dde_mlid,
185                                                  XEMACS_CLASS, 0);
186   mswindows_dde_topic_system = DdeCreateStringHandle (mswindows_dde_mlid,
187                                                       SZDDESYS_TOPIC, 0);
188   mswindows_dde_item_open = DdeCreateStringHandle (mswindows_dde_mlid,
189                                                    TEXT(MSWINDOWS_DDE_ITEM_OPEN), 0);
190   DdeNameService (mswindows_dde_mlid, mswindows_dde_service, 0L, DNS_REGISTER);
191 #endif
192 }
193
194 static void
195 mswindows_delete_device (struct device *d)
196 {
197 #ifdef HAVE_DRAGNDROP
198   DdeNameService (mswindows_dde_mlid, 0L, 0L, DNS_UNREGISTER);
199   DdeFreeStringHandle (mswindows_dde_mlid, mswindows_dde_item_open);
200   DdeFreeStringHandle (mswindows_dde_mlid, mswindows_dde_topic_system);
201   DdeFreeStringHandle (mswindows_dde_mlid, mswindows_dde_service);
202   DdeUninitialize (mswindows_dde_mlid);
203
204 # if !(defined(CYGWIN) || defined(MINGW))
205   CoUninitialize ();
206 # endif
207 #endif
208
209   DeleteDC (DEVICE_MSWINDOWS_HCDC(d));
210   xfree (d->device_data);
211 }
212
213 void
214 mswindows_get_workspace_coords (RECT *rc)
215 {
216   SystemParametersInfo (SPI_GETWORKAREA, 0, rc, 0);
217 }
218
219 static void
220 mswindows_mark_device (struct device *d)
221 {
222   mark_object (DEVICE_MSWINDOWS_FONTLIST (d));
223 }
224
225 static Lisp_Object
226 mswindows_device_system_metrics (struct device *d,
227                                  enum device_metrics m)
228 {
229   const HDC hdc = DEVICE_MSWINDOWS_HCDC(d);
230
231   switch (m)
232     {
233     case DM_size_device:
234       return Fcons (make_int (GetDeviceCaps (hdc, HORZRES)),
235                     make_int (GetDeviceCaps (hdc, VERTRES)));
236       break;
237     case DM_device_dpi:
238       return Fcons (make_int (GetDeviceCaps (hdc, LOGPIXELSX)),
239                     make_int (GetDeviceCaps (hdc, LOGPIXELSY)));
240       break;
241     case DM_size_device_mm:
242       return Fcons (make_int (GetDeviceCaps (hdc, HORZSIZE)),
243                     make_int (GetDeviceCaps (hdc, VERTSIZE)));
244       break;
245     case DM_num_bit_planes:
246       /* this is what X means by bitplanes therefore we ought to be
247          consistent. num planes is always 1 under mswindows and
248          therefore useless */
249       return make_int (GetDeviceCaps (hdc, BITSPIXEL));
250       break;
251     case DM_num_color_cells:
252       /* #### SIZEPALETTE only valid if RC_PALETTE bit set in RASTERCAPS,
253          what should we return for a non-palette-based device? */
254       return make_int (GetDeviceCaps (hdc, SIZEPALETTE));
255       break;
256
257       /*** Colors ***/
258 #define FROB(met, fore, back)                           \
259     case DM_##met:                                      \
260       return build_syscolor_cons (fore, back);
261       
262       FROB (color_default, COLOR_WINDOWTEXT, COLOR_WINDOW);
263       FROB (color_select, COLOR_HIGHLIGHTTEXT, COLOR_HIGHLIGHT);
264       FROB (color_balloon, COLOR_INFOTEXT, COLOR_INFOBK);
265       FROB (color_3d_face, COLOR_BTNTEXT, COLOR_BTNFACE);
266       FROB (color_3d_light, COLOR_3DHILIGHT, COLOR_3DLIGHT);
267       FROB (color_3d_dark, COLOR_3DDKSHADOW, COLOR_3DSHADOW);
268       FROB (color_menu, COLOR_MENUTEXT, COLOR_MENU);
269       FROB (color_menu_highlight, COLOR_HIGHLIGHTTEXT, COLOR_HIGHLIGHT);
270       FROB (color_menu_button, COLOR_MENUTEXT, COLOR_MENU);
271       FROB (color_menu_disabled, COLOR_GRAYTEXT, COLOR_MENU);
272       FROB (color_toolbar, COLOR_BTNTEXT, COLOR_BTNFACE);
273       FROB (color_scrollbar, COLOR_CAPTIONTEXT, COLOR_SCROLLBAR);
274       FROB (color_desktop, -1, COLOR_DESKTOP);
275       FROB (color_workspace, -1, COLOR_APPWORKSPACE);
276 #undef FROB
277
278       /*** Sizes ***/
279 #define FROB(met, index1, index2)                       \
280     case DM_##met:                                      \
281       return build_sysmetrics_cons (index1, index2);
282
283       FROB (size_cursor, SM_CXCURSOR, SM_CYCURSOR);
284       FROB (size_scrollbar, SM_CXVSCROLL, SM_CYHSCROLL);
285       FROB (size_menu, -1, SM_CYMENU);
286       FROB (size_icon, SM_CXICON, SM_CYICON);
287       FROB (size_icon_small, SM_CXSMICON, SM_CYSMICON);
288 #undef FROB
289
290     case DM_size_workspace:
291       {
292         RECT rc;
293         mswindows_get_workspace_coords (&rc);
294         return Fcons (make_int (rc.right - rc.left),
295                       make_int (rc.bottom - rc.top));
296       }
297
298     case DM_offset_workspace:
299       {
300         RECT rc;
301         mswindows_get_workspace_coords (&rc);
302         return Fcons (make_int (rc.left), make_int (rc.top));
303       }
304
305       /*
306         case DM_size_toolbar:
307         case DM_size_toolbar_button:
308         case DM_size_toolbar_border:
309       */
310
311       /*** Features ***/
312 #define FROB(met, index)                        \
313     case DM_##met:                              \
314       return make_int (GetSystemMetrics (index));
315
316       FROB (mouse_buttons, SM_CMOUSEBUTTONS);
317       FROB (swap_buttons, SM_SWAPBUTTON);
318       FROB (show_sounds, SM_SHOWSOUNDS);
319       FROB (slow_device, SM_SLOWMACHINE);
320       FROB (security, SM_SECURE);
321 #undef FROB
322
323     }
324
325   /* Do not know such property */
326   return Qunbound;
327 }
328
329 static unsigned int
330 mswindows_device_implementation_flags (void)
331 {
332   return XDEVIMPF_PIXEL_GEOMETRY;
333 }
334
335 \f
336 /************************************************************************/
337 /*                          printer helpers                             */
338 /************************************************************************/
339
340 static void
341 signal_open_printer_error (struct device *d)
342 {
343   signal_simple_error ("Failed to open printer", DEVICE_CONNECTION (d));
344 }
345
346
347 /* Helper function */
348 static int
349 msprinter_init_device_internal (struct device *d, char* printer_name)
350 {
351   DEVICE_MSPRINTER_NAME(d) = xstrdup (printer_name);
352
353   if (!OpenPrinter (printer_name, &DEVICE_MSPRINTER_HPRINTER (d), NULL))
354     {
355       DEVICE_MSPRINTER_HPRINTER (d) = NULL;
356       return 0;
357     }
358
359   DEVICE_MSPRINTER_HDC (d) = CreateDC ("WINSPOOL", printer_name,
360                                        NULL, NULL);
361   if (DEVICE_MSPRINTER_HDC (d) == NULL)
362     return 0;
363
364   DEVICE_MSPRINTER_HCDC(d) =
365     CreateCompatibleDC (DEVICE_MSPRINTER_HDC (d));
366
367   DEVICE_CLASS (d) = (GetDeviceCaps (DEVICE_MSPRINTER_HDC (d), BITSPIXEL)
368                       * GetDeviceCaps (DEVICE_MSPRINTER_HDC (d), PLANES)
369                       > 1) ? Qcolor : Qmono;
370   return 1;
371 }
372
373 static void
374 msprinter_delete_device_internal (struct device *d)
375 {
376   if (DEVICE_MSPRINTER_HPRINTER (d))
377     ClosePrinter (DEVICE_MSPRINTER_HPRINTER (d));
378   if (DEVICE_MSPRINTER_HDC (d))
379     DeleteDC (DEVICE_MSPRINTER_HDC (d));
380   if (DEVICE_MSPRINTER_HCDC (d))
381     DeleteDC (DEVICE_MSPRINTER_HCDC (d));
382   if (DEVICE_MSPRINTER_NAME (d))
383     xfree (DEVICE_MSPRINTER_NAME (d));
384
385   DEVICE_MSPRINTER_FONTLIST (d) = Qnil;
386 }
387
388 static int msprinter_reinit_device (struct device *d, char* devname)
389 {
390   msprinter_delete_device_internal (d);
391   return msprinter_init_device_internal (d, devname);
392 }
393
394 \f
395 /************************************************************************/
396 /*                          printer methods                             */
397 /************************************************************************/
398
399 static void
400 msprinter_init_device (struct device *d, Lisp_Object props)
401 {
402   char* printer_name;
403   DEVMODE *pdm;
404   size_t dm_size;
405
406   d->device_data = xnew_and_zero (struct msprinter_device);
407
408   DEVICE_INFD (d) = DEVICE_OUTFD (d) = -1;
409   DEVICE_MSPRINTER_DEVMODE(d) = Qnil;
410
411   /* We do not use printer fon list as we do with the display
412      device. Rather, we allow GDI to pick the closest match to the
413      display font. */
414   DEVICE_MSPRINTER_FONTLIST (d) = Qnil;
415
416   CHECK_STRING (DEVICE_CONNECTION (d));
417
418   TO_EXTERNAL_FORMAT (LISP_STRING, DEVICE_CONNECTION (d),
419                       C_STRING_ALLOCA, printer_name,
420                       Qmswindows_tstr);
421
422   if (!msprinter_init_device_internal (d, printer_name))
423     signal_open_printer_error (d);
424     
425   /* Determinie DEVMODE size and store the default DEVMODE */
426   dm_size = DocumentProperties (NULL, DEVICE_MSPRINTER_HPRINTER(d),
427                                 printer_name, NULL, NULL, 0);
428   if (dm_size <= 0)
429     signal_open_printer_error (d);
430
431   pdm = (DEVMODE*) xmalloc (dm_size);
432   DocumentProperties (NULL, DEVICE_MSPRINTER_HPRINTER(d),
433                       printer_name, pdm,
434                       NULL, DM_OUT_BUFFER);
435
436   assert (DEVMODE_SIZE (pdm) <= dm_size);
437
438   DEVICE_MSPRINTER_DEVMODE(d) = 
439     allocate_devmode (pdm, 0, printer_name, d);
440
441 }
442
443 static void
444 msprinter_delete_device (struct device *d)
445 {
446   if (d->device_data)
447     {
448       msprinter_delete_device_internal (d);
449
450       /* Disassociate the selected devmode with the device */
451       if (!NILP (DEVICE_MSPRINTER_DEVMODE (d)))
452         {
453           XDEVMODE (DEVICE_MSPRINTER_DEVMODE (d))->device = Qnil;
454           DEVICE_MSPRINTER_DEVMODE (d) = Qnil;
455         }
456
457       xfree (d->device_data);
458     }
459 }
460
461 static Lisp_Object
462 msprinter_device_system_metrics (struct device *d,
463                                  enum device_metrics m)
464 {
465   switch (m)
466     {
467       /* Device sizes - pixel and mm */
468 #define FROB(met, index1, index2)                       \
469     case DM_##met:                                      \
470       return build_devicecaps_cons                      \
471          (DEVICE_MSPRINTER_HDC(d), index1, index2);
472
473       FROB (size_device, PHYSICALWIDTH, PHYSICALHEIGHT);
474       FROB (size_device_mm, HORZSIZE, VERTSIZE);
475       FROB (size_workspace, HORZRES, VERTRES);
476       FROB (offset_workspace, PHYSICALOFFSETX, PHYSICALOFFSETY);
477       FROB (device_dpi, LOGPIXELSX, LOGPIXELSY);
478 #undef FROB
479
480     case DM_num_bit_planes:
481       /* this is what X means by bitplanes therefore we ought to be
482          consistent. num planes is always 1 under mswindows and
483          therefore useless */
484       return make_int (GetDeviceCaps (DEVICE_MSPRINTER_HDC(d), BITSPIXEL));
485
486     case DM_num_color_cells:    /* Prnters are non-palette devices */
487     case DM_slow_device:        /* Animation would be a really bad idea */
488     case DM_security:           /* Not provided by windows */
489       return Qzero;
490     }
491
492   /* Do not know such property */
493   return Qunbound;
494 }
495
496 static void
497 msprinter_mark_device (struct device *d)
498 {
499   mark_object (DEVICE_MSPRINTER_FONTLIST (d));
500   mark_object (DEVICE_MSPRINTER_DEVMODE (d));
501 }
502
503 static unsigned int
504 msprinter_device_implementation_flags (void)
505 {
506   return (  XDEVIMPF_PIXEL_GEOMETRY
507           | XDEVIMPF_IS_A_PRINTER
508           | XDEVIMPF_NO_AUTO_REDISPLAY
509           | XDEVIMPF_FRAMELESS_OK );
510 }
511 \f
512 /************************************************************************/
513 /*                      printer Lisp subroutines                        */
514 /************************************************************************/
515
516 static void
517 global_free_2_maybe (HGLOBAL hg1, HGLOBAL hg2)
518 {
519   if (hg1 != NULL)
520     GlobalFree (hg1);
521   if (hg2 != NULL)
522     GlobalFree (hg2);
523 }
524
525 static HGLOBAL
526 devmode_to_hglobal (Lisp_Devmode *ldm)
527 {
528   HGLOBAL hg = GlobalAlloc (GHND, XDEVMODE_SIZE (ldm));
529   memcpy (GlobalLock (hg), ldm->devmode, XDEVMODE_SIZE (ldm));
530   GlobalUnlock (hg);
531   return hg;
532 }
533
534 /* Returns 0 if the printer has been deleted due to a fatal I/O error,
535    1 otherwise. */
536 static int
537 sync_printer_with_devmode (struct device* d, DEVMODE* devmode_in,
538                            DEVMODE* devmode_out, char* devname)
539 {
540   /* Change connection if the device changed */
541   if (devname != NULL
542       && stricmp (devname, DEVICE_MSPRINTER_NAME(d)) != 0)
543     {
544       Lisp_Object new_connection = build_ext_string (devname, Qmswindows_tstr);
545       struct gcpro gcpro1;
546
547       GCPRO1 (new_connection);
548       DEVICE_CONNECTION (d) = Qnil;
549       if (!NILP (Ffind_device (new_connection, Qmsprinter)))
550         {
551           /* We are in trouble - second msprinter for the same device.
552              Nothing wrong on the Windows side, just forge a unique
553              connection name. Use the memory address of d as a unique
554              suffix. */
555           char* new_connext = alloca (strlen (devname + 11));
556           sprintf (new_connext, "%s:%X", devname, d->header.uid);
557           new_connection = build_ext_string (devname, Qmswindows_tstr);
558         }
559       DEVICE_CONNECTION (d) = new_connection;
560       UNGCPRO;
561
562       /* Reinitialize printer. The device can pop off in process */
563       if (!msprinter_reinit_device (d, devname))
564         {
565           /* Kaboom! */
566           delete_device_internal (d, 1, 0, 1);
567           return 0;
568         }
569     }
570
571   /* Apply the new devmode to the printer */
572   DocumentProperties (NULL,
573                       DEVICE_MSPRINTER_HPRINTER(d),
574                       DEVICE_MSPRINTER_NAME(d),
575                       devmode_out, devmode_in,
576                       DM_IN_BUFFER | DM_OUT_BUFFER);
577
578   /* #### ResetDC fails sometimes, Bill only knows why.
579      The solution below looks more like a workaround to me,
580      although it might be fine. --kkm */
581   if (ResetDC (DEVICE_MSPRINTER_HDC (d), devmode_out) == NULL)
582     {
583       DeleteDC (DEVICE_MSPRINTER_HDC (d));
584       DEVICE_MSPRINTER_HDC (d) =
585         CreateDC ("WINSPOOL", DEVICE_MSPRINTER_NAME(d), NULL, devmode_out);
586     }
587  
588   return 1;
589 }
590
591 static void
592 handle_devmode_changes (Lisp_Devmode *ldm, HGLOBAL hDevNames, HGLOBAL hDevMode)
593 {
594   DEVNAMES* devnames = (DEVNAMES*) GlobalLock (hDevNames);
595   char *new_name = devnames ? (char*)devnames + devnames->wDeviceOffset : NULL;
596   DEVMODE* devmode = (DEVMODE*) GlobalLock (hDevMode);
597
598   /* Size and name may have changed */
599   ldm->devmode = xrealloc (ldm->devmode, DEVMODE_SIZE (devmode));
600   if (new_name)
601     {
602       if (ldm->printer_name)
603         xfree (ldm->printer_name);
604       ldm->printer_name = xstrdup (new_name);
605     }
606
607   if (!NILP (ldm->device))
608     {
609       /* Apply the new devmode to the printer and get a compete one back */
610       struct device *d = XDEVICE (ldm->device);
611       if (!sync_printer_with_devmode (d, devmode, ldm->devmode, new_name))
612         {
613           global_free_2_maybe (hDevNames, hDevMode);
614           error ("Printer device initialization I/O error, device deleted.");
615         }
616     }
617   else
618     {
619       /* Just copy the devmode structure */
620       memcpy (ldm->devmode, devmode, DEVMODE_SIZE (devmode));
621     }
622 }
623
624 static void
625 ensure_not_printing (struct device *d)
626 {
627   if (!NILP (DEVICE_FRAME_LIST (d)))
628   {
629     Lisp_Object device;
630     XSETDEVICE (device, d);
631     signal_simple_error ("Cannot change settings while print job is active",
632                          device);
633   }
634 }
635
636 static Lisp_Devmode *
637 decode_devmode (Lisp_Object dev)
638 {
639   if (DEVMODEP (dev))
640     return XDEVMODE (dev);
641   else
642     {
643       struct device* d = decode_device (dev);
644       Lisp_Object device;
645       XSETDEVICE (device, d);
646       CHECK_MSPRINTER_DEVICE (device);
647       ensure_not_printing (d);
648       return XDEVMODE (DEVICE_MSPRINTER_DEVMODE (d));
649     }
650 }
651
652 /*
653  * DEV can be either a printer or devmode
654  * PRINT_P is non-zero for the Print dialog, zero for the
655  *         Page Setup dialog
656  */
657 static Lisp_Object
658 print_dialog_worker (Lisp_Object dev, int print_p)
659 {
660   Lisp_Devmode *ldm = decode_devmode (dev);
661   PRINTDLG pd;
662
663   memset (&pd, 0, sizeof (pd));
664   pd.lStructSize = sizeof (pd);
665   pd.hwndOwner = mswindows_get_selected_frame_hwnd ();
666   pd.hDevMode = devmode_to_hglobal (ldm);
667   pd.Flags = (PD_NOSELECTION | PD_USEDEVMODECOPIESANDCOLLATE
668               | (print_p ? 0 : PD_PRINTSETUP));
669   pd.nMinPage = 0;
670   pd.nMaxPage = 0xFFFF;
671
672   if (!PrintDlg (&pd))
673     {
674       global_free_2_maybe (pd.hDevNames, pd.hDevMode);
675       return Qnil;
676     }
677
678   handle_devmode_changes (ldm, pd.hDevNames, pd.hDevMode);
679
680   /* Finally, build the resulting plist */
681   {
682     Lisp_Object result = Qnil;
683     struct gcpro gcpro1;
684     GCPRO1 (result);
685
686     /* Do consing in reverse order.
687        Number of copies */
688     if (print_p)
689       result = Fcons (Qcopies, Fcons (make_int (pd.nCopies), result));
690
691     /* Page range */
692     if (print_p && (pd.Flags & PD_PAGENUMS))
693       {
694         result = Fcons (Qto_page, Fcons (make_int (pd.nToPage), result));
695         result = Fcons (Qfrom_page, Fcons (make_int (pd.nFromPage), result));
696       }
697
698     /* Device name */
699     result = Fcons (Qname,
700                     Fcons (build_ext_string (ldm->printer_name,
701                                              Qmswindows_tstr),
702                            result));
703     UNGCPRO;
704
705     global_free_2_maybe (pd.hDevNames, pd.hDevMode);
706     return result;
707   }
708 }
709
710 DEFUN ("msprinter-print-setup-dialog", Fmsprinter_print_setup_dialog, 1, 1, 0, /*
711 Invoke Windows standard Printer Setup dialog.
712 This dialog is usually invoked when the user selects the Printer Setup
713 command.
714
715 DEVICE must be either an 'msprinter device, or a printer settings
716 object. The function brings up the Printer Setup dialog, where the user
717 can select a different printer and/or change printer options.
718 Connection name can change as a result of selecting a different printer
719 device.  If a printer is specified, then changes are stored into the
720 settings object currently selected into that printer.  If a settings
721 object is supplied, then changes are recorded into it, and, it it is
722 selected into a printer, then changes are propagated to that printer
723 too.
724
725 Return value is nil if the user has canceled the dialog.  Otherwise, it
726 is a new plist, with the following properties:
727   name       Printer device name, even if unchanged by the user.
728
729 The printer device is destroyed and an error is signaled if new printer
730 is selected by the user, but cannot be initialized.
731
732 See also `msprinter-print-dialog' and `msprinter-page-setup-dialog'.
733 */
734         (device))
735 {
736   return print_dialog_worker (device, 0);
737 }
738
739 DEFUN ("msprinter-print-dialog", Fmsprinter_print_dialog, 1, 1, 0, /*
740 Invoke Windows standard Print dialog.
741 This dialog is usually invoked when the user selects the Print command.
742 After the user presses OK, the program should start actual printout.
743
744 DEVICE must be either an 'msprinter device, or a printer settings
745 object. The function brings up the Print dialog, where the user can
746 select a different printer and/or change printer options. Connection
747 name can change as a result of selecting a different printer device.  If
748 a printer is specified, then changes are stored into the settings object
749 currently selected into that printer.  If a settings object is supplied,
750 then changes are recorded into it, and, it it is selected into a
751 printer, then changes are propagated to that printer 
752 too.
753
754 Return value is nil if the user has canceled the dialog.  Otherwise, it
755 is a new plist, with the following properties:
756   name       Printer device name, even if unchanged by the user.
757   from-page  First page to print, 1-based. If not specified by the user,
758              then this value is not included in the plist.
759   to-page    Last page to print, inclusive, 1-based. If not specified by
760              the user, then this value is not included in the plist.
761   copies     Number of copies to print.  Always returned.
762
763 The DEVICE is destroyed and an error is signaled in case of
764 initialization problem with the new printer.
765
766 See also `msprinter-setup-print-dialog' and
767 `msprinter-page-setup-dialog'.
768 */
769         (device))
770 {
771   return print_dialog_worker (device, 1);
772 }
773
774
775 static int
776 plist_get_margin (Lisp_Object plist, Lisp_Object prop)
777 {
778   Lisp_Object val = Fplist_get (plist, prop, make_int (1440));
779   if (!INTP (val))
780     signal_simple_error ("Margin value must be an integer", val);
781
782   return MulDiv (XINT (val), 100, 144);
783 }
784
785 static Lisp_Object
786 plist_set_margin (Lisp_Object plist, Lisp_Object prop, int margin, int mm_p)
787 {
788   Lisp_Object val = make_int (MulDiv (margin, 144, mm_p ? 2450 : 100));
789   return Fcons (prop, Fcons (val, plist));
790 }
791
792 DEFUN ("msprinter-page-setup-dialog", Fmsprinter_page_setup_dialog, 1, 2, 0, /*
793 Invoke Windows standard Page Setup dialog.
794 This dialog is usually invoked in response to Page Setup command, and
795 used to chose such parameters as page orientation, print margins etc.
796 Note that this dialog contains the "Printer" button, which invokes
797 Printer Setup dialog (see `msprinter-print-setup-dialog') so that the
798 user can update the printer options or even select a different printer
799 as well.
800
801 DEVICE must be either an 'msprinter device, or a printer settings
802 object. The function brings up the Page Setup dialog, where the user
803 can select a different printer and/or change printer options.
804 Connection name can change as a result of selecting a different printer
805 device.  If a printer is specified, then changes are stored into the
806 settings object currently selected into that printer.  If a settings
807 object is supplied, then changes are recorded into it, and, it it is
808 selected into a printer, then changes are propagated to that printer
809 too.
810
811 PLIST is a plist of job properties;
812 see `default-msprinter-frame-plist' for the complete list.  The plist
813 is used to initialize the dialog.
814
815 Return value is nil if the user has canceled the dialog.  Otherwise,
816 it is a new plist, containing the new list of properties.
817
818 The DEVICE is destroyed and an error is signaled in case of
819 initialization problem with the new printer.
820
821 See also `msprinter-print-setup-dialog' and `msprinter-print-dialog'.
822 */
823        (device, plist))
824 {
825   Lisp_Devmode *ldm = decode_devmode (device);
826   PAGESETUPDLG pd;
827
828   memset (&pd, 0, sizeof (pd));
829   pd.lStructSize = sizeof (pd);
830   pd.hwndOwner = mswindows_get_selected_frame_hwnd ();
831   pd.Flags = PSD_MARGINS;
832   pd.rtMargin.left   = plist_get_margin (plist, Qleft_margin);
833   pd.rtMargin.top    = plist_get_margin (plist, Qtop_margin);
834   pd.rtMargin.right  = plist_get_margin (plist, Qright_margin);
835   pd.rtMargin.bottom = plist_get_margin (plist, Qbottom_margin);
836   pd.hDevMode = devmode_to_hglobal (ldm);
837
838   if (!PageSetupDlg (&pd))
839     {
840       global_free_2_maybe (pd.hDevNames, pd.hDevMode);
841       return Qnil;
842     }
843
844   if (pd.hDevMode)
845     handle_devmode_changes (ldm, pd.hDevNames, pd.hDevMode);
846
847   /* Finally, build the resulting plist */
848   {
849     Lisp_Object result = Qnil;
850     int mm_p = pd.Flags & PSD_INHUNDREDTHSOFMILLIMETERS;
851     result = plist_set_margin (result, Qbottom_margin, pd.rtMargin.bottom, mm_p);
852     result = plist_set_margin (result, Qright_margin, pd.rtMargin.right, mm_p);
853     result = plist_set_margin (result, Qtop_margin, pd.rtMargin.top, mm_p);
854     result = plist_set_margin (result, Qleft_margin, pd.rtMargin.left, mm_p);
855     return result;
856   }
857 }
858
859 DEFUN ("msprinter-get-settings", Fmsprinter_get_settings, 1, 1, 0, /*
860 Return the settings object currently used by DEVICE.
861 The object returned is not a copy, but rather a pointer to the
862 original one. Use `msprinter-settings-copy' to create a copy of it.
863 */
864         (device))
865 {
866   struct device *d = decode_device (device);
867   XSETDEVICE (device, d);
868   CHECK_MSPRINTER_DEVICE (device);
869   return DEVICE_MSPRINTER_DEVMODE (d);
870 }
871
872 DEFUN ("msprinter-select-settings", Fmsprinter_select_settings, 2, 2, 0, /*
873 Select SETTINGS object into a DEVICE.
874 The settings from the settings object are immediately applied to the
875 printer, possibly changing even the target printer itself, and all
876 future changes are applied synchronously to the printer device and the
877 selected printer object, until a different settings object is selected
878 into the same printer.
879
880 A settings object can be selected to no more than one printer at a time.
881
882 If the supplied settings object is not specialized, it is specialized
883 for the printer immediately upon selection. The object can be
884 despecialized after it is unselected by calling the function
885 `msprinter-settings-despecialize'. 
886
887 Return value is the previously selected settings object.
888 */
889         (device, settings))
890 {
891   Lisp_Devmode *ldm;
892   struct device *d = decode_device (device);
893
894   struct gcpro gcpro1;
895   GCPRO1 (settings);
896
897   XSETDEVICE (device, d);
898   CHECK_MSPRINTER_DEVICE (device);
899   CHECK_DEVMODE (settings);
900   ldm = XDEVMODE (settings);
901
902   if (!NILP (ldm->device))
903     signal_simple_error ("The object is currently selected into a device",
904                          settings);
905
906   /* If the object being selected is de-specialized, then its
907      size is perhaps not enough to receive the new devmode. We can ask
908      for printer's devmode size here, because despecialized settings
909      cannot force switching to a different printer, as they supply no
910      printer name at all. */
911   if (ldm->printer_name == NULL)
912     {
913       size_t dm_size =
914         DocumentProperties (NULL, DEVICE_MSPRINTER_HPRINTER(d),
915                             DEVICE_MSPRINTER_NAME(d), NULL, NULL, 0);
916       if (dm_size <= 0)
917         signal_simple_error ("Unable to specialize settings, printer error",
918                              device);
919
920       assert (XDEVMODE_SIZE (ldm) <= dm_size);
921       ldm->devmode = xrealloc (ldm->devmode, dm_size);
922     }
923
924   /* If we bail out on signal here, no damage is done, except that
925      the stirage for the DEVMODE structure might be reallocated to
926      hold a larger one - not a big deal */
927   if (!sync_printer_with_devmode (d, ldm->devmode, ldm->devmode,
928                                   ldm->printer_name))
929     error ("Printer device initialization I/O error, device deleted.");
930
931   if (ldm->printer_name == NULL)
932     ldm->printer_name = xstrdup (DEVICE_MSPRINTER_NAME(d));
933
934   {
935     Lisp_Object old_mode = DEVICE_MSPRINTER_DEVMODE (d);
936     ldm->device = device;
937     XDEVMODE (old_mode)->device = Qnil;
938     DEVICE_MSPRINTER_DEVMODE (d) = settings;
939     UNGCPRO;
940     return old_mode;
941   }
942 }
943
944 DEFUN ("msprinter-apply-settings", Fmsprinter_apply_settings, 2, 2, 0, /*
945 Apply settings from a SETTINGS object to a 'msprinter DEVICE.
946 The settings from the settings object are immediately applied to the
947 printer, possibly changing even the target printer itself. The SETTING
948 object is not modified, unlike `msprinter-select-settings', and the
949 supplied object is not changed.  The changes are immediately recorded
950 into the settings object which is currently selected into the printer
951 device.
952
953 Return value is the currently selected settings object.
954 */
955         (device, settings))
956 {
957   Lisp_Devmode *ldm_current, *ldm_new;
958   struct device *d = decode_device (device);
959
960   struct gcpro gcpro1;
961   GCPRO1 (settings);
962
963   XSETDEVICE (device, d);
964   CHECK_MSPRINTER_DEVICE (device);
965   CHECK_DEVMODE (settings);
966   ldm_new = XDEVMODE (settings);
967   ldm_current = XDEVMODE (DEVICE_MSPRINTER_DEVMODE (d));
968
969   /* If the supplied devmode is not specialized, then the current
970      devmode size will always be sufficient, as the printer does
971      not change.  If it is specialized, we must reallocate the cuttent
972      devmode storage to match with the supplied one, as it has the right
973      size for the new printer, if it is going to change.  The correct
974      way is to use the largest of the two though, to keep the old
975      contents unchanged in case of preliminary exit.
976   */
977   if (ldm_new->printer_name)
978     ldm_current->devmode =
979       (DEVMODE*) xrealloc (ldm_current->devmode,
980                            max (XDEVMODE_SIZE (ldm_new),
981                                 XDEVMODE_SIZE (ldm_current)));
982
983   if (!sync_printer_with_devmode (d, ldm_new->devmode,
984                                   ldm_current->devmode,
985                                   ldm_new->printer_name))
986     error ("Printer device initialization I/O error, device deleted.");
987   
988   if (ldm_new->printer_name != NULL)
989     {
990       xfree (ldm_current->printer_name);
991       ldm_current->printer_name = xstrdup (ldm_new->printer_name);
992     }
993
994   return DEVICE_MSPRINTER_DEVMODE (d);
995 }
996 \f
997 /************************************************************************/
998 /*                                devmode                               */
999 /************************************************************************/
1000
1001 static void
1002 print_devmode (Lisp_Object obj, Lisp_Object printcharfun,
1003                int escapeflag)
1004 {
1005   char buf[100];
1006   Lisp_Devmode *dm = XDEVMODE (obj);
1007   if (print_readably)
1008     error ("printing unreadable object #<msprinter-settings 0x%x>",
1009            dm->header.uid);
1010   write_c_string ("#<msprinter-settings", printcharfun);
1011   if (dm->printer_name)
1012     {
1013       write_c_string (" for \"", printcharfun);
1014       write_c_string (dm->printer_name, printcharfun);
1015       write_c_string ("\"", printcharfun);
1016     }
1017   if (!NILP (dm->device))
1018     {
1019       write_c_string (" (currently on ", printcharfun);
1020       print_internal (dm->device, printcharfun, 0);
1021       write_c_string (")", printcharfun);
1022     }
1023   sprintf (buf, " 0x%x>", dm->header.uid);
1024   write_c_string (buf, printcharfun);
1025 }
1026
1027 static void
1028 finalize_devmode (void *header, int for_disksave)
1029 {
1030   Lisp_Devmode *dm = (Lisp_Devmode *) header;
1031
1032   if (for_disksave)
1033     {
1034       Lisp_Object devmode;
1035       XSETDEVMODE (devmode, dm);
1036       signal_simple_error (
1037         "Cannot dump XEmacs containing an msprinter-settings object",
1038         devmode);
1039     }
1040
1041   assert (NILP (dm->device));
1042
1043   if (dm->printer_name)
1044     xfree (dm->printer_name);
1045 }
1046
1047 static int
1048 equal_devmode (Lisp_Object obj1, Lisp_Object obj2, int depth)
1049 {
1050   Lisp_Devmode *dm1 = XDEVMODE (obj1);
1051   Lisp_Devmode *dm2 = XDEVMODE (obj2);
1052
1053   if ((dm1->devmode != NULL) != (dm1->devmode != NULL))
1054     return 0;
1055   if (dm1->devmode == NULL)
1056     return 1;
1057   if (memcmp (dm1->devmode, dm2->devmode, XDEVMODE_SIZE (dm1)) != 0)
1058     return 0;
1059   if (dm1->printer_name == NULL || dm2->printer_name == NULL)
1060     return 1;
1061   return stricmp (dm1->printer_name, dm2->printer_name) == 0;
1062 }
1063
1064 static unsigned long
1065 hash_devmode (Lisp_Object obj, int depth)
1066 {
1067   Lisp_Devmode *dm = XDEVMODE (obj);
1068
1069   return HASH3 (XDEVMODE_SIZE (dm),
1070                 dm->devmode ? memory_hash (dm->devmode, XDEVMODE_SIZE (dm))
1071                 : 0,
1072                 dm->printer_name ? string_hash (dm->printer_name) : 0);
1073 }
1074
1075 DEFINE_LRECORD_IMPLEMENTATION ("msprinter-settings", devmode,
1076                                0/*mark*/, print_devmode, finalize_devmode,
1077                                equal_devmode, hash_devmode, 0/*description*/,
1078                                Lisp_Devmode);
1079 static Lisp_Object
1080 allocate_devmode (DEVMODE* src_devmode, int do_copy,
1081                   char* src_name, struct device *d)
1082 {
1083   Lisp_Devmode *dm;
1084   Lisp_Object ob;
1085
1086   dm = alloc_lcrecord_type (Lisp_Devmode, &lrecord_devmode);
1087
1088   if (d)
1089     XSETDEVICE (dm->device, d);
1090   else
1091     dm->device = Qnil;
1092
1093   dm->printer_name = src_name ? xstrdup (src_name) : NULL;
1094
1095   if (src_devmode != NULL && do_copy)
1096     {
1097       dm->devmode = (DEVMODE*) xmalloc (DEVMODE_SIZE (src_devmode));
1098       memcpy (dm->devmode, src_devmode, DEVMODE_SIZE (src_devmode));
1099     }
1100   else
1101     {
1102       dm->devmode = src_devmode;
1103     }
1104
1105   XSETDEVMODE (ob, dm);
1106   return ob;
1107 }
1108
1109 DEFUN ("msprinter-settings-copy", Fmsprinter_settings_copy, 1, 1, 0, /*
1110 Create and returns an exact copy of a printer settings object.
1111 */
1112        (settings))
1113 {
1114   Lisp_Devmode *dm;
1115
1116   CHECK_DEVMODE (settings);
1117   dm = XDEVMODE (settings);
1118
1119   return allocate_devmode (dm->devmode, 1, dm->printer_name, NULL);
1120 }
1121
1122 DEFUN ("msprinter-settings-despecialize", Fmsprinter_settings_despecialize, 1, 1, 0, /*
1123 Erase printer-specific settings from a printer settings object.
1124 */
1125        (settings))
1126 {
1127   Lisp_Devmode *ldm;
1128   DEVMODE *dm;
1129
1130   CHECK_DEVMODE (settings);
1131   ldm = XDEVMODE (settings);
1132
1133   if (!NILP (ldm->device))
1134     signal_simple_error ("The object is currently selected into a device",
1135                          settings);
1136
1137   dm = ldm->devmode;
1138
1139   /* #### TODO. Either remove references to device specific bins,
1140      paper sizes etc, or signal an error of they are present. */
1141
1142   dm->dmDriverExtra = 0;
1143   dm->dmDeviceName[0] = '\0';
1144
1145   if (ldm->printer_name)
1146     xfree (ldm->printer_name);
1147
1148   return Qnil;
1149 }
1150
1151 \f
1152 /************************************************************************/
1153 /*                            initialization                            */
1154 /************************************************************************/
1155
1156 void
1157 syms_of_device_mswindows (void)
1158 {
1159   INIT_LRECORD_IMPLEMENTATION (devmode);
1160
1161   DEFSUBR (Fmsprinter_print_setup_dialog);
1162   DEFSUBR (Fmsprinter_print_dialog);
1163   DEFSUBR (Fmsprinter_page_setup_dialog);
1164   DEFSUBR (Fmsprinter_get_settings);
1165   DEFSUBR (Fmsprinter_select_settings);
1166   DEFSUBR (Fmsprinter_apply_settings);
1167   DEFSUBR (Fmsprinter_settings_copy);
1168   DEFSUBR (Fmsprinter_settings_despecialize);
1169
1170   defsymbol (&Qinit_pre_mswindows_win, "init-pre-mswindows-win");
1171   defsymbol (&Qinit_post_mswindows_win, "init-post-mswindows-win");
1172 }
1173
1174 void
1175 console_type_create_device_mswindows (void)
1176 {
1177   CONSOLE_HAS_METHOD (mswindows, init_device);
1178   CONSOLE_HAS_METHOD (mswindows, finish_init_device);
1179   CONSOLE_HAS_METHOD (mswindows, mark_device);
1180   CONSOLE_HAS_METHOD (mswindows, delete_device);
1181   CONSOLE_HAS_METHOD (mswindows, device_system_metrics);
1182   CONSOLE_HAS_METHOD (mswindows, device_implementation_flags);
1183
1184   CONSOLE_HAS_METHOD (msprinter, init_device);
1185   CONSOLE_HAS_METHOD (msprinter, mark_device);
1186   CONSOLE_HAS_METHOD (msprinter, delete_device);
1187   CONSOLE_HAS_METHOD (msprinter, device_system_metrics);
1188   CONSOLE_HAS_METHOD (msprinter, device_implementation_flags);
1189 }
1190
1191
1192 void
1193 vars_of_device_mswindows (void)
1194 {
1195   DEFVAR_LISP ("mswindows-downcase-file-names", &Vmswindows_downcase_file_names /*
1196 Non-nil means convert all-upper case file names to lower case.
1197 This applies when performing completions and file name expansion.
1198 */ );
1199   Vmswindows_downcase_file_names = Qnil;
1200
1201   DEFVAR_LISP ("mswindows-get-true-file-attributes", &Vmswindows_get_true_file_attributes /*
1202 Non-nil means determine accurate link count in file-attributes.
1203 This option slows down file-attributes noticeably, so is disabled by
1204 default.  Note that it is only useful for files on NTFS volumes,
1205 where hard links are supported.
1206 */ );
1207   Vmswindows_get_true_file_attributes = Qnil;
1208 }