XEmacs 21.4.2 "Developer-Friendly Unix APIs".
[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 int
796 mswindows_get_default_margin (Lisp_Object prop)
797 {
798   if (EQ (prop, Qleft_margin)) return 1440;
799   if (EQ (prop, Qright_margin)) return 1440;
800   if (EQ (prop, Qtop_margin)) return 720;
801   if (EQ (prop, Qbottom_margin)) return 720;
802   abort ();
803   return 0;
804 }
805
806 static int
807 plist_get_margin (Lisp_Object plist, Lisp_Object prop)
808 {
809   Lisp_Object val =
810     Fplist_get (plist, prop, make_int (mswindows_get_default_margin (prop)));
811   if (!INTP (val))
812     invalid_argument ("Margin value must be an integer", val);
813
814   return MulDiv (XINT (val), 100, 144);
815 }
816
817 static Lisp_Object
818 plist_set_margin (Lisp_Object plist, Lisp_Object prop, int margin, int mm_p)
819 {
820   Lisp_Object val = make_int (MulDiv (margin, 144, mm_p ? 2450 : 100));
821   return Fcons (prop, Fcons (val, plist));
822 }
823
824 Lisp_Object
825 mswindows_handle_page_setup_dialog_box (struct frame *f, Lisp_Object keys)
826 {
827   Lisp_Object device = Qunbound, settings = Qunbound;
828   Lisp_Object plist = Qnil;
829
830   {
831     EXTERNAL_PROPERTY_LIST_LOOP_3 (key, value, keys)
832       {
833         if (EQ (key, Q_device))
834           {
835             device = wrap_device (decode_device (value));
836             CHECK_MSPRINTER_DEVICE (device);
837           }
838         else if (EQ (key, Q_printer_settings))
839           {
840             CHECK_DEVMODE (value);
841             settings = value;
842           }
843         else if (EQ (key, Q_properties))
844           {
845             CHECK_LIST (value);
846             plist = value;
847           }
848         else
849           syntax_error ("Unrecognized page-setup dialog keyword", key);
850       }
851   }
852
853   if ((UNBOUNDP (device) && UNBOUNDP (settings)) ||
854       (!UNBOUNDP (device) && !UNBOUNDP (settings)))
855     syntax_error ("Exactly one of :device and :printer-settings must be given",
856                   keys);
857
858   if (UNBOUNDP (device))
859     device = settings;
860
861   {
862     Lisp_Devmode *ldm = decode_devmode (device);
863     PAGESETUPDLG pd;
864
865     memset (&pd, 0, sizeof (pd));
866     pd.lStructSize = sizeof (pd);
867     pd.hwndOwner = mswindows_get_selected_frame_hwnd ();
868     pd.Flags = PSD_MARGINS;
869     pd.rtMargin.left   = plist_get_margin (plist, Qleft_margin);
870     pd.rtMargin.top    = plist_get_margin (plist, Qtop_margin);
871     pd.rtMargin.right  = plist_get_margin (plist, Qright_margin);
872     pd.rtMargin.bottom = plist_get_margin (plist, Qbottom_margin);
873     pd.hDevMode = devmode_to_hglobal (ldm);
874
875     if (!PageSetupDlg (&pd))
876       {
877         global_free_2_maybe (pd.hDevNames, pd.hDevMode);
878         return Qnil;
879       }
880
881     if (pd.hDevMode)
882       handle_devmode_changes (ldm, pd.hDevNames, pd.hDevMode);
883
884     /* Finally, build the resulting plist */
885     {
886       Lisp_Object result = Qnil;
887       int mm_p = pd.Flags & PSD_INHUNDREDTHSOFMILLIMETERS;
888       result = plist_set_margin (result, Qbottom_margin, pd.rtMargin.bottom,
889                                  mm_p);
890       result = plist_set_margin (result, Qright_margin, pd.rtMargin.right,
891                                  mm_p);
892       result = plist_set_margin (result, Qtop_margin, pd.rtMargin.top, mm_p);
893       result = plist_set_margin (result, Qleft_margin, pd.rtMargin.left, mm_p);
894       return result;
895     }
896   }
897 }
898
899 DEFUN ("msprinter-get-settings", Fmsprinter_get_settings, 1, 1, 0, /*
900 Return the settings object currently used by DEVICE.
901 The object returned is not a copy, but rather a pointer to the
902 original one. Use `msprinter-settings-copy' to create a copy of it.
903 */
904         (device))
905 {
906   struct device *d = decode_device (device);
907   XSETDEVICE (device, d);
908   CHECK_MSPRINTER_DEVICE (device);
909   return DEVICE_MSPRINTER_DEVMODE (d);
910 }
911
912 DEFUN ("msprinter-select-settings", Fmsprinter_select_settings, 2, 2, 0, /*
913 Select SETTINGS object into a DEVICE.
914 The settings from the settings object are immediately applied to the
915 printer, possibly changing even the target printer itself, and all
916 future changes are applied synchronously to the printer device and the
917 selected printer object, until a different settings object is selected
918 into the same printer.
919
920 A settings object can be selected to no more than one printer at a time.
921
922 If the supplied settings object is not specialized, it is specialized
923 for the printer immediately upon selection. The object can be
924 despecialized after it is unselected by calling the function
925 `msprinter-settings-despecialize'.
926
927 Return value is the previously selected settings object.
928 */
929         (device, settings))
930 {
931   Lisp_Devmode *ldm;
932   struct device *d = decode_device (device);
933
934   struct gcpro gcpro1;
935   GCPRO1 (settings);
936
937   XSETDEVICE (device, d);
938   CHECK_MSPRINTER_DEVICE (device);
939   CHECK_DEVMODE (settings);
940   ldm = XDEVMODE (settings);
941
942   if (!NILP (ldm->device))
943     invalid_operation ("The object is currently selected into a device",
944                        settings);
945
946   /* If the object being selected is de-specialized, then its
947      size is perhaps not enough to receive the new devmode. We can ask
948      for printer's devmode size here, because despecialized settings
949      cannot force switching to a different printer, as they supply no
950      printer name at all. */
951   if (ldm->printer_name == NULL)
952     {
953       size_t dm_size =
954         DocumentProperties (NULL, DEVICE_MSPRINTER_HPRINTER(d),
955                             DEVICE_MSPRINTER_NAME(d), NULL, NULL, 0);
956       if (dm_size <= 0)
957         invalid_operation ("Unable to specialize settings, printer error",
958                            device);
959
960       assert (XDEVMODE_SIZE (ldm) <= dm_size);
961       ldm->devmode = xrealloc (ldm->devmode, dm_size);
962     }
963
964   /* If we bail out on signal here, no damage is done, except that
965      the storage for the DEVMODE structure might be reallocated to
966      hold a larger one - not a big deal */
967   if (!sync_printer_with_devmode (d, ldm->devmode, ldm->devmode,
968                                   ldm->printer_name))
969     error ("Printer device initialization I/O error, device deleted.");
970
971   if (ldm->printer_name == NULL)
972     ldm->printer_name = xstrdup (DEVICE_MSPRINTER_NAME(d));
973
974   {
975     Lisp_Object old_mode = DEVICE_MSPRINTER_DEVMODE (d);
976     ldm->device = device;
977     XDEVMODE (old_mode)->device = Qnil;
978     DEVICE_MSPRINTER_DEVMODE (d) = settings;
979     UNGCPRO;
980     return old_mode;
981   }
982 }
983
984 DEFUN ("msprinter-apply-settings", Fmsprinter_apply_settings, 2, 2, 0, /*
985 Apply settings from a SETTINGS object to a 'msprinter DEVICE.
986 The settings from the settings object are immediately applied to the
987 printer, possibly changing even the target printer itself. The SETTING
988 object is not modified, unlike `msprinter-select-settings', and the
989 supplied object is not changed.  The changes are immediately recorded
990 into the settings object which is currently selected into the printer
991 device.
992
993 Return value is the currently selected settings object.
994 */
995         (device, settings))
996 {
997   Lisp_Devmode *ldm_current, *ldm_new;
998   struct device *d = decode_device (device);
999
1000   struct gcpro gcpro1;
1001   GCPRO1 (settings);
1002
1003   XSETDEVICE (device, d);
1004   CHECK_MSPRINTER_DEVICE (device);
1005   CHECK_DEVMODE (settings);
1006   ldm_new = XDEVMODE (settings);
1007   ldm_current = XDEVMODE (DEVICE_MSPRINTER_DEVMODE (d));
1008
1009   /* If the supplied devmode is not specialized, then the current
1010      devmode size will always be sufficient, as the printer does
1011      not change.  If it is specialized, we must reallocate the current
1012      devmode storage to match with the supplied one, as it has the right
1013      size for the new printer, if it is going to change.  The correct
1014      way is to use the largest of the two though, to keep the old
1015      contents unchanged in case of preliminary exit.
1016   */
1017   if (ldm_new->printer_name)
1018     ldm_current->devmode =
1019       (DEVMODE*) xrealloc (ldm_current->devmode,
1020                            max (XDEVMODE_SIZE (ldm_new),
1021                                 XDEVMODE_SIZE (ldm_current)));
1022
1023   if (!sync_printer_with_devmode (d, ldm_new->devmode,
1024                                   ldm_current->devmode,
1025                                   ldm_new->printer_name))
1026     error ("Printer device initialization I/O error, device deleted.");
1027
1028   if (ldm_new->printer_name != NULL)
1029     {
1030       xfree (ldm_current->printer_name);
1031       ldm_current->printer_name = xstrdup (ldm_new->printer_name);
1032     }
1033
1034   UNGCPRO;
1035   return DEVICE_MSPRINTER_DEVMODE (d);
1036 }
1037 \f
1038 /************************************************************************/
1039 /*                                devmode                               */
1040 /************************************************************************/
1041
1042 static void
1043 print_devmode (Lisp_Object obj, Lisp_Object printcharfun,
1044                int escapeflag)
1045 {
1046   char buf[100];
1047   Lisp_Devmode *dm = XDEVMODE (obj);
1048   if (print_readably)
1049     error ("printing unreadable object #<msprinter-settings 0x%x>",
1050            dm->header.uid);
1051   write_c_string ("#<msprinter-settings", printcharfun);
1052   if (dm->printer_name)
1053     {
1054       write_c_string (" for \"", printcharfun);
1055       write_c_string (dm->printer_name, printcharfun);
1056       write_c_string ("\"", printcharfun);
1057     }
1058   if (!NILP (dm->device))
1059     {
1060       write_c_string (" (currently on ", printcharfun);
1061       print_internal (dm->device, printcharfun, 0);
1062       write_c_string (")", printcharfun);
1063     }
1064   sprintf (buf, " 0x%x>", dm->header.uid);
1065   write_c_string (buf, printcharfun);
1066 }
1067
1068 static void
1069 finalize_devmode (void *header, int for_disksave)
1070 {
1071   Lisp_Devmode *dm = (Lisp_Devmode *) header;
1072
1073   if (for_disksave)
1074     {
1075       Lisp_Object devmode;
1076       XSETDEVMODE (devmode, dm);
1077       invalid_operation
1078         ("Cannot dump XEmacs containing an msprinter-settings object",
1079          devmode);
1080     }
1081
1082   assert (NILP (dm->device));
1083
1084   if (dm->printer_name)
1085     xfree (dm->printer_name);
1086 }
1087
1088 static int
1089 equal_devmode (Lisp_Object obj1, Lisp_Object obj2, int depth)
1090 {
1091   Lisp_Devmode *dm1 = XDEVMODE (obj1);
1092   Lisp_Devmode *dm2 = XDEVMODE (obj2);
1093
1094   if ((dm1->devmode != NULL) != (dm1->devmode != NULL))
1095     return 0;
1096   if (dm1->devmode == NULL)
1097     return 1;
1098   if (memcmp (dm1->devmode, dm2->devmode, XDEVMODE_SIZE (dm1)) != 0)
1099     return 0;
1100   if (dm1->printer_name == NULL || dm2->printer_name == NULL)
1101     return 1;
1102   return stricmp (dm1->printer_name, dm2->printer_name) == 0;
1103 }
1104
1105 static unsigned long
1106 hash_devmode (Lisp_Object obj, int depth)
1107 {
1108   Lisp_Devmode *dm = XDEVMODE (obj);
1109
1110   return HASH3 (XDEVMODE_SIZE (dm),
1111                 dm->devmode ? memory_hash (dm->devmode, XDEVMODE_SIZE (dm))
1112                 : 0,
1113                 dm->printer_name ? string_hash (dm->printer_name) : 0);
1114 }
1115
1116 DEFINE_LRECORD_IMPLEMENTATION ("msprinter-settings", devmode,
1117                                0/*mark*/, print_devmode, finalize_devmode,
1118                                equal_devmode, hash_devmode, 0/*description*/,
1119                                Lisp_Devmode);
1120 static Lisp_Object
1121 allocate_devmode (DEVMODE* src_devmode, int do_copy,
1122                   char* src_name, struct device *d)
1123 {
1124   Lisp_Devmode *dm;
1125   Lisp_Object ob;
1126
1127   dm = alloc_lcrecord_type (Lisp_Devmode, &lrecord_devmode);
1128
1129   if (d)
1130     XSETDEVICE (dm->device, d);
1131   else
1132     dm->device = Qnil;
1133
1134   dm->printer_name = src_name ? xstrdup (src_name) : NULL;
1135
1136   if (src_devmode != NULL && do_copy)
1137     {
1138       dm->devmode = (DEVMODE*) xmalloc (DEVMODE_SIZE (src_devmode));
1139       memcpy (dm->devmode, src_devmode, DEVMODE_SIZE (src_devmode));
1140     }
1141   else
1142     {
1143       dm->devmode = src_devmode;
1144     }
1145
1146   XSETDEVMODE (ob, dm);
1147   return ob;
1148 }
1149
1150 DEFUN ("msprinter-settings-copy", Fmsprinter_settings_copy, 1, 1, 0, /*
1151 Create and returns an exact copy of a printer settings object.
1152 */
1153        (settings))
1154 {
1155   Lisp_Devmode *dm;
1156
1157   CHECK_DEVMODE (settings);
1158   dm = XDEVMODE (settings);
1159
1160   return allocate_devmode (dm->devmode, 1, dm->printer_name, NULL);
1161 }
1162
1163 DEFUN ("msprinter-settings-despecialize", Fmsprinter_settings_despecialize, 1, 1, 0, /*
1164 Erase printer-specific settings from a printer settings object.
1165 */
1166        (settings))
1167 {
1168   Lisp_Devmode *ldm;
1169   DEVMODE *dm;
1170
1171   CHECK_DEVMODE (settings);
1172   ldm = XDEVMODE (settings);
1173
1174   if (!NILP (ldm->device))
1175     invalid_operation ("The object is currently selected into a device",
1176                        settings);
1177
1178   dm = ldm->devmode;
1179
1180   /* #### TODO. Either remove references to device specific bins,
1181      paper sizes etc, or signal an error of they are present. */
1182
1183   dm->dmDriverExtra = 0;
1184   dm->dmDeviceName[0] = '\0';
1185
1186   if (ldm->printer_name)
1187     xfree (ldm->printer_name);
1188
1189   return Qnil;
1190 }
1191
1192 DEFUN ("mswindows-get-default-printer", Fmswindows_get_default_printer, 0, 0, 0, /*
1193 Return name of the default printer, as string, on nil if there is no default.
1194 */
1195        ())
1196 {
1197   return msprinter_default_printer ();
1198 }
1199
1200 static void
1201 signal_enum_printer_error (void)
1202 {
1203   invalid_operation ("Error enumerating printers", make_int (GetLastError ()));
1204 }
1205
1206 DEFUN ("mswindows-printer-list", Fmswindows_printer_list, 0, 0, 0, /*
1207 Return a list of string names of installed printers.
1208 If there is a default printer, it is returned as the first element of
1209 the list.  If there is no default printer, the first element of the
1210 list will be nil.  The rest of elements are guaranteed to have string
1211 values.  Return value is nil if there are no printers installed.
1212 */
1213        ())
1214 {
1215   int have_nt, ok;
1216   BYTE *data_buf, dummy_byte;
1217   size_t enum_entry_size;
1218   DWORD enum_flags, enum_level, bytes_needed, num_printers;
1219   struct gcpro gcpro1, gcpro2;
1220   Lisp_Object result = Qnil, def_printer = Qnil;
1221
1222   /* Determine OS flavor, to use the fastest enumeration method available */
1223   have_nt = !mswindows_windows9x_p ();
1224   enum_flags = PRINTER_ENUM_LOCAL | (have_nt ? PRINTER_ENUM_CONNECTIONS : 0);
1225   enum_level = have_nt ? 4 : 5;
1226   enum_entry_size = have_nt ? sizeof (PRINTER_INFO_4) : sizeof (PRINTER_INFO_5);
1227
1228   /* Allocate memory for printer enum structure */
1229   ok = EnumPrinters (enum_flags, NULL, enum_level, &dummy_byte, 1,
1230                      &bytes_needed, &num_printers);
1231   if (ok)
1232     /* No printers, if just 1 byte is enough */
1233     return Qnil;
1234
1235   if (GetLastError () != ERROR_INSUFFICIENT_BUFFER)
1236     signal_enum_printer_error ();
1237
1238   data_buf = alloca (bytes_needed);
1239   ok = EnumPrinters (enum_flags, NULL, enum_level, data_buf, bytes_needed,
1240                      &bytes_needed, &num_printers);
1241   if (!ok)
1242     signal_enum_printer_error ();
1243
1244   if (num_printers == 0)
1245     /* Strange but... */
1246     return Qnil;
1247
1248   GCPRO2 (result, def_printer);
1249
1250   while (num_printers--)
1251     {
1252       LPCTSTR printer_name;
1253       if (have_nt)
1254         {
1255           PRINTER_INFO_4 *info = (PRINTER_INFO_4*) data_buf;
1256           printer_name = info->pPrinterName;
1257         }
1258       else
1259         {
1260           PRINTER_INFO_5 *info = (PRINTER_INFO_5*) data_buf;
1261           printer_name = info->pPrinterName;
1262         }
1263       data_buf += enum_entry_size;
1264
1265       result = Fcons (build_ext_string (printer_name, Qmswindows_tstr),
1266                       result);
1267     }
1268
1269   def_printer = msprinter_default_printer ();
1270   result = Fdelete (def_printer, result);
1271   result = Fcons (def_printer, result);
1272
1273   RETURN_UNGCPRO (result);
1274 }
1275
1276 \f
1277 /************************************************************************/
1278 /*                            initialization                            */
1279 /************************************************************************/
1280
1281 void
1282 syms_of_device_mswindows (void)
1283 {
1284   INIT_LRECORD_IMPLEMENTATION (devmode);
1285
1286   DEFSUBR (Fmsprinter_get_settings);
1287   DEFSUBR (Fmsprinter_select_settings);
1288   DEFSUBR (Fmsprinter_apply_settings);
1289   DEFSUBR (Fmsprinter_settings_copy);
1290   DEFSUBR (Fmsprinter_settings_despecialize);
1291   DEFSUBR (Fmswindows_get_default_printer);
1292   DEFSUBR (Fmswindows_printer_list);
1293
1294   defsymbol (&Qinit_pre_mswindows_win, "init-pre-mswindows-win");
1295   defsymbol (&Qinit_post_mswindows_win, "init-post-mswindows-win");
1296 }
1297
1298 void
1299 console_type_create_device_mswindows (void)
1300 {
1301   CONSOLE_HAS_METHOD (mswindows, init_device);
1302   CONSOLE_HAS_METHOD (mswindows, finish_init_device);
1303   CONSOLE_HAS_METHOD (mswindows, mark_device);
1304   CONSOLE_HAS_METHOD (mswindows, delete_device);
1305   CONSOLE_HAS_METHOD (mswindows, device_system_metrics);
1306   CONSOLE_HAS_METHOD (mswindows, device_implementation_flags);
1307
1308   CONSOLE_HAS_METHOD (msprinter, init_device);
1309   CONSOLE_HAS_METHOD (msprinter, mark_device);
1310   CONSOLE_HAS_METHOD (msprinter, delete_device);
1311   CONSOLE_HAS_METHOD (msprinter, device_system_metrics);
1312   CONSOLE_HAS_METHOD (msprinter, device_implementation_flags);
1313 }
1314
1315
1316 void
1317 vars_of_device_mswindows (void)
1318 {
1319   DEFVAR_LISP ("mswindows-downcase-file-names", &Vmswindows_downcase_file_names /*
1320 Non-nil means convert all-upper case file names to lower case.
1321 This applies when performing completions and file name expansion.
1322 */ );
1323   Vmswindows_downcase_file_names = Qnil;
1324
1325   DEFVAR_LISP ("mswindows-get-true-file-attributes", &Vmswindows_get_true_file_attributes /*
1326 Non-nil means determine accurate link count in file-attributes.
1327 This option slows down file-attributes noticeably, so is disabled by
1328 default.  Note that it is only useful for files on NTFS volumes,
1329 where hard links are supported.
1330 */ );
1331   Vmswindows_get_true_file_attributes = Qnil;
1332 }