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