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