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