comparison src/device-msw.c @ 410:de805c49cfc1 r21-2-35

Import from CVS: tag r21-2-35
author cvs
date Mon, 13 Aug 2007 11:19:21 +0200
parents 501cfd01ee6d
children 697ef44129c6
comparison
equal deleted inserted replaced
409:301b9ebbdf3b 410:de805c49cfc1
38 #include "events.h" 38 #include "events.h"
39 #include "faces.h" 39 #include "faces.h"
40 #include "frame.h" 40 #include "frame.h"
41 #include "sysdep.h" 41 #include "sysdep.h"
42 42
43 #if !(defined (__CYGWIN32__) || defined(__MINGW32__)) 43 /* #### Andy, these includes might break cygwin compilation - kkm*/
44 #include <commdlg.h>
45 #include <winspool.h>
46
47 #if !(defined (CYGWIN) || defined(MINGW))
44 # include <objbase.h> /* For CoInitialize */ 48 # include <objbase.h> /* For CoInitialize */
45 #endif 49 #endif
46 50
47 /* win32 DDE management library globals */ 51 /* win32 DDE management library globals */
48 #ifdef HAVE_DRAGNDROP 52 #ifdef HAVE_DRAGNDROP
60 exactly, at the expense of slower operation. Since true hard links 64 exactly, at the expense of slower operation. Since true hard links
61 are supported on NTFS volumes, this is only relevant on NT. */ 65 are supported on NTFS volumes, this is only relevant on NT. */
62 Lisp_Object Vmswindows_get_true_file_attributes; 66 Lisp_Object Vmswindows_get_true_file_attributes;
63 67
64 Lisp_Object Qinit_pre_mswindows_win, Qinit_post_mswindows_win; 68 Lisp_Object Qinit_pre_mswindows_win, Qinit_post_mswindows_win;
65 69 Lisp_Object Qdevmodep;
70
71 static Lisp_Object allocate_devmode (DEVMODE* src_devmode, int do_copy,
72 char* src_name, struct device *d);
66 73
67 /************************************************************************/ 74 /************************************************************************/
68 /* helpers */ 75 /* helpers */
69 /************************************************************************/ 76 /************************************************************************/
70 77
97 { 104 {
98 return Fcons (index1 < 0 ? Qnil : make_int (GetDeviceCaps (hdc, index1)), 105 return Fcons (index1 < 0 ? Qnil : make_int (GetDeviceCaps (hdc, index1)),
99 index2 < 0 ? Qnil : make_int (GetDeviceCaps (hdc, index2))); 106 index2 < 0 ? Qnil : make_int (GetDeviceCaps (hdc, index2)));
100 } 107 }
101 108
102
103 109
104 /************************************************************************/ 110 /************************************************************************/
105 /* display methods */ 111 /* display methods */
106 /************************************************************************/ 112 /************************************************************************/
107 113
117 init_one_device (d); 123 init_one_device (d);
118 124
119 d->device_data = xnew_and_zero (struct mswindows_device); 125 d->device_data = xnew_and_zero (struct mswindows_device);
120 hdc = CreateCompatibleDC (NULL); 126 hdc = CreateCompatibleDC (NULL);
121 assert (hdc!=NULL); 127 assert (hdc!=NULL);
122 DEVICE_MSWINDOWS_LOGPIXELSX(d) = GetDeviceCaps(hdc, LOGPIXELSX); 128 DEVICE_MSWINDOWS_HCDC(d) = hdc;
123 DEVICE_MSWINDOWS_LOGPIXELSY(d) = GetDeviceCaps(hdc, LOGPIXELSY);
124 DEVICE_MSWINDOWS_PLANES(d) = GetDeviceCaps(hdc, PLANES);
125 /* #### SIZEPALETTE only valid if RC_PALETTE bit set in RASTERCAPS,
126 what should we return for a non-palette-based device? */
127 DEVICE_MSWINDOWS_CELLS(d) = GetDeviceCaps(hdc, SIZEPALETTE);
128 DEVICE_MSWINDOWS_HORZRES(d) = GetDeviceCaps(hdc, HORZRES);
129 DEVICE_MSWINDOWS_VERTRES(d) = GetDeviceCaps(hdc, VERTRES);
130 DEVICE_MSWINDOWS_HORZSIZE(d) = GetDeviceCaps(hdc, HORZSIZE);
131 DEVICE_MSWINDOWS_VERTSIZE(d) = GetDeviceCaps(hdc, VERTSIZE);
132 DEVICE_MSWINDOWS_BITSPIXEL(d) = GetDeviceCaps(hdc, BITSPIXEL);
133 DEVICE_MSWINDOWS_FONTLIST (d) = mswindows_enumerate_fonts (hdc); 129 DEVICE_MSWINDOWS_FONTLIST (d) = mswindows_enumerate_fonts (hdc);
134 130 DEVICE_MSWINDOWS_UPDATE_TICK (d) = GetTickCount ();
135 DEVICE_MSWINDOWS_HCDC(d) = hdc;
136 131
137 /* Register the main window class */ 132 /* Register the main window class */
138 wc.cbSize = sizeof (WNDCLASSEX); 133 wc.cbSize = sizeof (WNDCLASSEX);
139 wc.style = CS_OWNDC; /* One DC per window */ 134 wc.style = CS_OWNDC; /* One DC per window */
140 wc.lpfnWndProc = (WNDPROC) mswindows_wnd_proc; 135 wc.lpfnWndProc = (WNDPROC) mswindows_wnd_proc;
174 mswindows_finish_init_device (struct device *d, Lisp_Object props) 169 mswindows_finish_init_device (struct device *d, Lisp_Object props)
175 { 170 {
176 /* Initialize DDE management library and our related globals. We execute a 171 /* Initialize DDE management library and our related globals. We execute a
177 * dde Open("file") by simulating a drop, so this depends on dnd support. */ 172 * dde Open("file") by simulating a drop, so this depends on dnd support. */
178 #ifdef HAVE_DRAGNDROP 173 #ifdef HAVE_DRAGNDROP
179 # if !(defined(__CYGWIN32__) || defined(__MINGW32__)) 174 # if !(defined(CYGWIN) || defined(MINGW))
180 CoInitialize (NULL); 175 CoInitialize (NULL);
181 # endif 176 # endif
182 177
183 mswindows_dde_mlid = 0; 178 mswindows_dde_mlid = 0;
184 DdeInitialize (&mswindows_dde_mlid, (PFNCALLBACK)mswindows_dde_callback, 179 DdeInitialize (&mswindows_dde_mlid, (PFNCALLBACK)mswindows_dde_callback,
204 DdeFreeStringHandle (mswindows_dde_mlid, mswindows_dde_item_open); 199 DdeFreeStringHandle (mswindows_dde_mlid, mswindows_dde_item_open);
205 DdeFreeStringHandle (mswindows_dde_mlid, mswindows_dde_topic_system); 200 DdeFreeStringHandle (mswindows_dde_mlid, mswindows_dde_topic_system);
206 DdeFreeStringHandle (mswindows_dde_mlid, mswindows_dde_service); 201 DdeFreeStringHandle (mswindows_dde_mlid, mswindows_dde_service);
207 DdeUninitialize (mswindows_dde_mlid); 202 DdeUninitialize (mswindows_dde_mlid);
208 203
209 # if !(defined(__CYGWIN32__) || defined(__MINGW32__)) 204 # if !(defined(CYGWIN) || defined(MINGW))
210 CoUninitialize (); 205 CoUninitialize ();
211 # endif 206 # endif
212 #endif 207 #endif
213 208
214 DeleteDC (DEVICE_MSWINDOWS_HCDC(d)); 209 DeleteDC (DEVICE_MSWINDOWS_HCDC(d));
215 free (d->device_data); 210 xfree (d->device_data);
216 } 211 }
217 212
218 void 213 void
219 msw_get_workspace_coords (RECT *rc) 214 mswindows_get_workspace_coords (RECT *rc)
220 { 215 {
221 SystemParametersInfo (SPI_GETWORKAREA, 0, rc, 0); 216 SystemParametersInfo (SPI_GETWORKAREA, 0, rc, 0);
222 } 217 }
223 218
224 static void 219 static void
229 224
230 static Lisp_Object 225 static Lisp_Object
231 mswindows_device_system_metrics (struct device *d, 226 mswindows_device_system_metrics (struct device *d,
232 enum device_metrics m) 227 enum device_metrics m)
233 { 228 {
229 const HDC hdc = DEVICE_MSWINDOWS_HCDC(d);
230
234 switch (m) 231 switch (m)
235 { 232 {
236 case DM_size_device: 233 case DM_size_device:
237 return Fcons (make_int (DEVICE_MSWINDOWS_HORZRES(d)), 234 return Fcons (make_int (GetDeviceCaps (hdc, HORZRES)),
238 make_int (DEVICE_MSWINDOWS_VERTRES(d))); 235 make_int (GetDeviceCaps (hdc, VERTRES)));
239 break; 236 break;
240 case DM_device_dpi: 237 case DM_device_dpi:
241 return Fcons (make_int (DEVICE_MSWINDOWS_LOGPIXELSX(d)), 238 return Fcons (make_int (GetDeviceCaps (hdc, LOGPIXELSX)),
242 make_int (DEVICE_MSWINDOWS_LOGPIXELSY(d))); 239 make_int (GetDeviceCaps (hdc, LOGPIXELSY)));
243 break; 240 break;
244 case DM_size_device_mm: 241 case DM_size_device_mm:
245 return Fcons (make_int (DEVICE_MSWINDOWS_HORZSIZE(d)), 242 return Fcons (make_int (GetDeviceCaps (hdc, HORZSIZE)),
246 make_int (DEVICE_MSWINDOWS_VERTSIZE(d))); 243 make_int (GetDeviceCaps (hdc, VERTSIZE)));
247 break; 244 break;
248 case DM_num_bit_planes: 245 case DM_num_bit_planes:
249 /* this is what X means by bitplanes therefore we ought to be 246 /* this is what X means by bitplanes therefore we ought to be
250 consistent. num planes is always 1 under mswindows and 247 consistent. num planes is always 1 under mswindows and
251 therefore useless */ 248 therefore useless */
252 return make_int (DEVICE_MSWINDOWS_BITSPIXEL(d)); 249 return make_int (GetDeviceCaps (hdc, BITSPIXEL));
253 break; 250 break;
254 case DM_num_color_cells: 251 case DM_num_color_cells:
255 return make_int (DEVICE_MSWINDOWS_CELLS(d)); 252 /* #### SIZEPALETTE only valid if RC_PALETTE bit set in RASTERCAPS,
253 what should we return for a non-palette-based device? */
254 return make_int (GetDeviceCaps (hdc, SIZEPALETTE));
256 break; 255 break;
257 256
258 /*** Colors ***/ 257 /*** Colors ***/
259 #define FROB(met, fore, back) \ 258 #define FROB(met, fore, back) \
260 case DM_##met: \ 259 case DM_##met: \
289 #undef FROB 288 #undef FROB
290 289
291 case DM_size_workspace: 290 case DM_size_workspace:
292 { 291 {
293 RECT rc; 292 RECT rc;
294 msw_get_workspace_coords (&rc); 293 mswindows_get_workspace_coords (&rc);
295 return Fcons (make_int (rc.right - rc.left), 294 return Fcons (make_int (rc.right - rc.left),
296 make_int (rc.bottom - rc.top)); 295 make_int (rc.bottom - rc.top));
297 } 296 }
298 297
299 case DM_offset_workspace: 298 case DM_offset_workspace:
300 { 299 {
301 RECT rc; 300 RECT rc;
302 msw_get_workspace_coords (&rc); 301 mswindows_get_workspace_coords (&rc);
303 return Fcons (make_int (rc.left), make_int (rc.top)); 302 return Fcons (make_int (rc.left), make_int (rc.top));
304 } 303 }
305 304
306 /* 305 /*
307 case DM_size_toolbar: 306 case DM_size_toolbar:
333 return XDEVIMPF_PIXEL_GEOMETRY; 332 return XDEVIMPF_PIXEL_GEOMETRY;
334 } 333 }
335 334
336 335
337 /************************************************************************/ 336 /************************************************************************/
338 /* printer methods */ 337 /* printer helpers */
339 /************************************************************************/ 338 /************************************************************************/
340 339
341 static void 340 static void
342 signal_open_printer_error (struct device *d) 341 signal_open_printer_error (struct device *d)
343 { 342 {
344 signal_simple_error ("Failed to open printer", DEVICE_CONNECTION (d)); 343 signal_simple_error ("Failed to open printer", DEVICE_CONNECTION (d));
345 } 344 }
346 345
347 static void 346
348 msprinter_init_device (struct device *d, Lisp_Object props) 347 /* Helper function */
349 { 348 static int
350 char* printer_name; 349 msprinter_init_device_internal (struct device *d, char* printer_name)
351 350 {
352 DEVICE_INFD (d) = DEVICE_OUTFD (d) = -1;
353
354 CHECK_STRING (DEVICE_CONNECTION (d));
355
356 TO_EXTERNAL_FORMAT (LISP_STRING, DEVICE_CONNECTION (d),
357 C_STRING_ALLOCA, printer_name,
358 Qctext);
359
360 d->device_data = xnew_and_zero (struct msprinter_device);
361
362 DEVICE_MSPRINTER_NAME(d) = xstrdup (printer_name); 351 DEVICE_MSPRINTER_NAME(d) = xstrdup (printer_name);
363 352
364 if (!OpenPrinter (printer_name, &DEVICE_MSPRINTER_HPRINTER (d), NULL)) 353 if (!OpenPrinter (printer_name, &DEVICE_MSPRINTER_HPRINTER (d), NULL))
365 { 354 {
366 DEVICE_MSPRINTER_HPRINTER (d) = NULL; 355 DEVICE_MSPRINTER_HPRINTER (d) = NULL;
367 signal_open_printer_error (d); 356 return 0;
368 } 357 }
369 358
370 DEVICE_MSPRINTER_HDC (d) = CreateDC ("WINSPOOL", printer_name, 359 DEVICE_MSPRINTER_HDC (d) = CreateDC ("WINSPOOL", printer_name,
371 NULL, NULL); 360 NULL, NULL);
372 if (DEVICE_MSPRINTER_HDC (d) == NULL) 361 if (DEVICE_MSPRINTER_HDC (d) == NULL)
373 signal_open_printer_error (d); 362 return 0;
374 363
375 DEVICE_MSPRINTER_HCDC(d) = 364 DEVICE_MSPRINTER_HCDC(d) =
376 CreateCompatibleDC (DEVICE_MSPRINTER_HDC (d)); 365 CreateCompatibleDC (DEVICE_MSPRINTER_HDC (d));
377 366
378 /* Determinie DEVMODE size and store the default DEVMODE */ 367 DEVICE_CLASS (d) = (GetDeviceCaps (DEVICE_MSPRINTER_HDC (d), BITSPIXEL)
379 DEVICE_MSPRINTER_DEVMODE_SIZE(d) = 368 * GetDeviceCaps (DEVICE_MSPRINTER_HDC (d), PLANES)
380 DocumentProperties (NULL, DEVICE_MSPRINTER_HPRINTER(d), 369 > 1) ? Qcolor : Qmono;
381 printer_name, NULL, NULL, 0); 370 return 1;
382 if (DEVICE_MSPRINTER_DEVMODE_SIZE(d) <= 0) 371 }
383 signal_open_printer_error (d); 372
384 373 static void
385 DEVICE_MSPRINTER_DEVMODE(d) = 374 msprinter_delete_device_internal (struct device *d)
386 (DEVMODE*) xmalloc (DEVICE_MSPRINTER_DEVMODE_SIZE(d)); 375 {
387 DocumentProperties (NULL, DEVICE_MSPRINTER_HPRINTER(d), 376 if (DEVICE_MSPRINTER_HPRINTER (d))
388 printer_name, DEVICE_MSPRINTER_DEVMODE(d), 377 ClosePrinter (DEVICE_MSPRINTER_HPRINTER (d));
389 NULL, DM_OUT_BUFFER); 378 if (DEVICE_MSPRINTER_HDC (d))
379 DeleteDC (DEVICE_MSPRINTER_HDC (d));
380 if (DEVICE_MSPRINTER_HCDC (d))
381 DeleteDC (DEVICE_MSPRINTER_HCDC (d));
382 if (DEVICE_MSPRINTER_NAME (d))
383 xfree (DEVICE_MSPRINTER_NAME (d));
384
385 DEVICE_MSPRINTER_FONTLIST (d) = Qnil;
386 }
387
388 static int msprinter_reinit_device (struct device *d, char* devname)
389 {
390 msprinter_delete_device_internal (d);
391 return msprinter_init_device_internal (d, devname);
392 }
393
394
395 /************************************************************************/
396 /* printer methods */
397 /************************************************************************/
398
399 static void
400 msprinter_init_device (struct device *d, Lisp_Object props)
401 {
402 char* printer_name;
403 DEVMODE *pdm;
404 size_t dm_size;
405
406 d->device_data = xnew_and_zero (struct msprinter_device);
407
408 DEVICE_INFD (d) = DEVICE_OUTFD (d) = -1;
409 DEVICE_MSPRINTER_DEVMODE(d) = Qnil;
390 410
391 /* We do not use printer fon list as we do with the display 411 /* We do not use printer fon list as we do with the display
392 device. Rather, we allow GDI to pick the closest match to the 412 device. Rather, we allow GDI to pick the closest match to the
393 display font. */ 413 display font. */
394 DEVICE_MSPRINTER_FONTLIST (d) = Qnil; 414 DEVICE_MSPRINTER_FONTLIST (d) = Qnil;
395 415
396 DEVICE_CLASS (d) = (GetDeviceCaps (DEVICE_MSPRINTER_HDC (d), BITSPIXEL) 416 CHECK_STRING (DEVICE_CONNECTION (d));
397 * GetDeviceCaps (DEVICE_MSPRINTER_HDC (d), PLANES) 417
398 > 1) ? Qcolor : Qmono; 418 TO_EXTERNAL_FORMAT (LISP_STRING, DEVICE_CONNECTION (d),
419 C_STRING_ALLOCA, printer_name,
420 Qmswindows_tstr);
421
422 if (!msprinter_init_device_internal (d, printer_name))
423 signal_open_printer_error (d);
424
425 /* Determinie DEVMODE size and store the default DEVMODE */
426 dm_size = DocumentProperties (NULL, DEVICE_MSPRINTER_HPRINTER(d),
427 printer_name, NULL, NULL, 0);
428 if (dm_size <= 0)
429 signal_open_printer_error (d);
430
431 pdm = (DEVMODE*) xmalloc (dm_size);
432 DocumentProperties (NULL, DEVICE_MSPRINTER_HPRINTER(d),
433 printer_name, pdm,
434 NULL, DM_OUT_BUFFER);
435
436 assert (DEVMODE_SIZE (pdm) <= dm_size);
437
438 DEVICE_MSPRINTER_DEVMODE(d) =
439 allocate_devmode (pdm, 0, printer_name, d);
440
441 }
442
443 static void
444 msprinter_delete_device (struct device *d)
445 {
446 if (d->device_data)
447 {
448 msprinter_delete_device_internal (d);
449
450 /* Disassociate the selected devmode with the device */
451 if (!NILP (DEVICE_MSPRINTER_DEVMODE (d)))
452 {
453 XDEVMODE (DEVICE_MSPRINTER_DEVMODE (d))->device = Qnil;
454 DEVICE_MSPRINTER_DEVMODE (d) = Qnil;
455 }
456
457 xfree (d->device_data);
458 }
399 } 459 }
400 460
401 static Lisp_Object 461 static Lisp_Object
402 msprinter_device_system_metrics (struct device *d, 462 msprinter_device_system_metrics (struct device *d,
403 enum device_metrics m) 463 enum device_metrics m)
432 /* Do not know such property */ 492 /* Do not know such property */
433 return Qunbound; 493 return Qunbound;
434 } 494 }
435 495
436 static void 496 static void
437 msprinter_delete_device (struct device *d)
438 {
439 if (d->device_data)
440 {
441 if (DEVICE_MSPRINTER_HPRINTER (d))
442 ClosePrinter (DEVICE_MSPRINTER_HPRINTER (d));
443 if (DEVICE_MSPRINTER_HDC (d))
444 DeleteDC (DEVICE_MSPRINTER_HDC (d));
445 if (DEVICE_MSPRINTER_HCDC (d))
446 DeleteDC (DEVICE_MSPRINTER_HCDC (d));
447 if (DEVICE_MSPRINTER_NAME (d))
448 free (DEVICE_MSPRINTER_NAME (d));
449 if (DEVICE_MSPRINTER_DEVMODE (d))
450 free (DEVICE_MSPRINTER_DEVMODE (d));
451 if (DEVICE_MSPRINTER_DEVMODE_MIRROR (d))
452 free (DEVICE_MSPRINTER_DEVMODE_MIRROR (d));
453
454 free (d->device_data);
455 }
456 }
457
458 static void
459 msprinter_mark_device (struct device *d) 497 msprinter_mark_device (struct device *d)
460 { 498 {
461 mark_object (DEVICE_MSPRINTER_FONTLIST (d)); 499 mark_object (DEVICE_MSPRINTER_FONTLIST (d));
500 mark_object (DEVICE_MSPRINTER_DEVMODE (d));
462 } 501 }
463 502
464 static unsigned int 503 static unsigned int
465 msprinter_device_implementation_flags (void) 504 msprinter_device_implementation_flags (void)
466 { 505 {
467 return ( XDEVIMPF_PIXEL_GEOMETRY 506 return ( XDEVIMPF_PIXEL_GEOMETRY
468 | XDEVIMPF_IS_A_PRINTER 507 | XDEVIMPF_IS_A_PRINTER
469 | XDEVIMPF_NO_AUTO_REDISPLAY 508 | XDEVIMPF_NO_AUTO_REDISPLAY
470 | XDEVIMPF_FRAMELESS_OK ); 509 | XDEVIMPF_FRAMELESS_OK );
471 } 510 }
472
473 511
474 /************************************************************************/ 512 /************************************************************************/
475 /* printer external functions */ 513 /* printer Lisp subroutines */
476 /************************************************************************/ 514 /************************************************************************/
477 515
478 /* 516 static void
479 * Return a copy of default DEVMODE. The copy returned is in 517 global_free_2_maybe (HGLOBAL hg1, HGLOBAL hg2)
480 * a static buffer which will be overwritten by next call. 518 {
481 */ 519 if (hg1 != NULL)
482 DEVMODE* 520 GlobalFree (hg1);
483 msprinter_get_devmode_copy (struct device *d) 521 if (hg2 != NULL)
484 { 522 GlobalFree (hg2);
485 assert (DEVICE_MSPRINTER_P (d)); 523 }
486 524
487 if (DEVICE_MSPRINTER_DEVMODE_MIRROR(d) == NULL) 525 static HGLOBAL
488 DEVICE_MSPRINTER_DEVMODE_MIRROR(d) = 526 devmode_to_hglobal (Lisp_Devmode *ldm)
489 (DEVMODE*) xmalloc (DEVICE_MSPRINTER_DEVMODE_SIZE(d)); 527 {
490 528 HGLOBAL hg = GlobalAlloc (GHND, XDEVMODE_SIZE (ldm));
491 memcpy (DEVICE_MSPRINTER_DEVMODE_MIRROR(d), 529 memcpy (GlobalLock (hg), ldm->devmode, XDEVMODE_SIZE (ldm));
492 DEVICE_MSPRINTER_DEVMODE(d), 530 GlobalUnlock (hg);
493 DEVICE_MSPRINTER_DEVMODE_SIZE(d)); 531 return hg;
494 532 }
495 return DEVICE_MSPRINTER_DEVMODE_MIRROR(d); 533
496 } 534 /* Returns 0 if the printer has been deleted due to a fatal I/O error,
497 535 1 otherwise. */
498 /* 536 static int
499 * Apply settings from the DEVMODE. The settings are considered 537 sync_printer_with_devmode (struct device* d, DEVMODE* devmode_in,
500 * incremental to the default DEVMODE, so that changes in the 538 DEVMODE* devmode_out, char* devname)
501 * passed structure supercede parameters of the printer. 539 {
502 * 540 /* Change connection if the device changed */
503 * The passed structure is overwritten by the fuction call; 541 if (devname != NULL
504 * complete printer settings are returned. 542 && stricmp (devname, DEVICE_MSPRINTER_NAME(d)) != 0)
505 */ 543 {
506 void 544 Lisp_Object new_connection = build_ext_string (devname, Qmswindows_tstr);
507 msprinter_apply_devmode (struct device *d, DEVMODE *devmode) 545 struct gcpro gcpro1;
508 { 546
509 assert (DEVICE_MSPRINTER_P (d)); 547 GCPRO1 (new_connection);
510 548 DEVICE_CONNECTION (d) = Qnil;
549 if (!NILP (Ffind_device (new_connection, Qmsprinter)))
550 {
551 /* We are in trouble - second msprinter for the same device.
552 Nothing wrong on the Windows side, just forge a unique
553 connection name. Use the memory address of d as a unique
554 suffix. */
555 char* new_connext = alloca (strlen (devname + 11));
556 sprintf (new_connext, "%s:%X", devname, d->header.uid);
557 new_connection = build_ext_string (devname, Qmswindows_tstr);
558 }
559 DEVICE_CONNECTION (d) = new_connection;
560 UNGCPRO;
561
562 /* Reinitialize printer. The device can pop off in process */
563 if (!msprinter_reinit_device (d, devname))
564 {
565 /* Kaboom! */
566 delete_device_internal (d, 1, 0, 1);
567 return 0;
568 }
569 }
570
571 /* Apply the new devmode to the printer */
511 DocumentProperties (NULL, 572 DocumentProperties (NULL,
512 DEVICE_MSPRINTER_HPRINTER(d), 573 DEVICE_MSPRINTER_HPRINTER(d),
513 DEVICE_MSPRINTER_NAME(d), 574 DEVICE_MSPRINTER_NAME(d),
514 devmode, devmode, 575 devmode_out, devmode_in,
515 DM_IN_BUFFER | DM_OUT_BUFFER); 576 DM_IN_BUFFER | DM_OUT_BUFFER);
516 577
517 /* #### ResetDC fails sometimes, Bill only know s why. 578 /* #### ResetDC fails sometimes, Bill only knows why.
518 The solution below looks more like a workaround to me, 579 The solution below looks more like a workaround to me,
519 although it might be fine. --kkm */ 580 although it might be fine. --kkm */
520 if (ResetDC (DEVICE_MSPRINTER_HDC (d), devmode) == NULL) 581 if (ResetDC (DEVICE_MSPRINTER_HDC (d), devmode_out) == NULL)
521 { 582 {
522 DeleteDC (DEVICE_MSPRINTER_HDC (d)); 583 DeleteDC (DEVICE_MSPRINTER_HDC (d));
523 DEVICE_MSPRINTER_HDC (d) = 584 DEVICE_MSPRINTER_HDC (d) =
524 CreateDC ("WINSPOOL", DEVICE_MSPRINTER_NAME(d), NULL, devmode); 585 CreateDC ("WINSPOOL", DEVICE_MSPRINTER_NAME(d), NULL, devmode_out);
525 } 586 }
587
588 return 1;
589 }
590
591 static void
592 handle_devmode_changes (Lisp_Devmode *ldm, HGLOBAL hDevNames, HGLOBAL hDevMode)
593 {
594 DEVNAMES* devnames = (DEVNAMES*) GlobalLock (hDevNames);
595 char *new_name = devnames ? (char*)devnames + devnames->wDeviceOffset : NULL;
596 DEVMODE* devmode = (DEVMODE*) GlobalLock (hDevMode);
597
598 /* Size and name may have changed */
599 ldm->devmode = xrealloc (ldm->devmode, DEVMODE_SIZE (devmode));
600 if (new_name)
601 {
602 if (ldm->printer_name)
603 xfree (ldm->printer_name);
604 ldm->printer_name = xstrdup (new_name);
605 }
606
607 if (!NILP (ldm->device))
608 {
609 /* Apply the new devmode to the printer and get a compete one back */
610 struct device *d = XDEVICE (ldm->device);
611 if (!sync_printer_with_devmode (d, devmode, ldm->devmode, new_name))
612 {
613 global_free_2_maybe (hDevNames, hDevMode);
614 error ("Printer device initialization I/O error, device deleted.");
615 }
616 }
617 else
618 {
619 /* Just copy the devmode structure */
620 memcpy (ldm->devmode, devmode, DEVMODE_SIZE (devmode));
621 }
622 }
623
624 static void
625 ensure_not_printing (struct device *d)
626 {
627 if (!NILP (DEVICE_FRAME_LIST (d)))
628 {
629 Lisp_Object device;
630 XSETDEVICE (device, d);
631 signal_simple_error ("Cannot change settings while print job is active",
632 device);
633 }
634 }
635
636 static Lisp_Devmode *
637 decode_devmode (Lisp_Object dev)
638 {
639 if (DEVMODEP (dev))
640 return XDEVMODE (dev);
641 else
642 {
643 struct device* d = decode_device (dev);
644 Lisp_Object device;
645 XSETDEVICE (device, d);
646 CHECK_MSPRINTER_DEVICE (device);
647 ensure_not_printing (d);
648 return XDEVMODE (DEVICE_MSPRINTER_DEVMODE (d));
649 }
650 }
651
652 /*
653 * DEV can be either a printer or devmode
654 * PRINT_P is non-zero for the Print dialog, zero for the
655 * Page Setup dialog
656 */
657 static Lisp_Object
658 print_dialog_worker (Lisp_Object dev, int print_p)
659 {
660 Lisp_Devmode *ldm = decode_devmode (dev);
661 PRINTDLG pd;
662
663 memset (&pd, 0, sizeof (pd));
664 pd.lStructSize = sizeof (pd);
665 pd.hwndOwner = mswindows_get_selected_frame_hwnd ();
666 pd.hDevMode = devmode_to_hglobal (ldm);
667 pd.Flags = (PD_NOSELECTION | PD_USEDEVMODECOPIESANDCOLLATE
668 | (print_p ? 0 : PD_PRINTSETUP));
669 pd.nMinPage = 0;
670 pd.nMaxPage = 0xFFFF;
671
672 if (!PrintDlg (&pd))
673 {
674 global_free_2_maybe (pd.hDevNames, pd.hDevMode);
675 return Qnil;
676 }
677
678 handle_devmode_changes (ldm, pd.hDevNames, pd.hDevMode);
679
680 /* Finally, build the resulting plist */
681 {
682 Lisp_Object result = Qnil;
683 struct gcpro gcpro1;
684 GCPRO1 (result);
685
686 /* Do consing in reverse order.
687 Number of copies */
688 if (print_p)
689 result = Fcons (Qcopies, Fcons (make_int (pd.nCopies), result));
690
691 /* Page range */
692 if (print_p && (pd.Flags & PD_PAGENUMS))
693 {
694 result = Fcons (Qto_page, Fcons (make_int (pd.nToPage), result));
695 result = Fcons (Qfrom_page, Fcons (make_int (pd.nFromPage), result));
696 }
697
698 /* Device name */
699 result = Fcons (Qname,
700 Fcons (build_ext_string (ldm->printer_name,
701 Qmswindows_tstr),
702 result));
703 UNGCPRO;
704
705 global_free_2_maybe (pd.hDevNames, pd.hDevMode);
706 return result;
707 }
708 }
709
710 DEFUN ("msprinter-print-setup-dialog", Fmsprinter_print_setup_dialog, 1, 1, 0, /*
711 Invoke Windows standard Printer Setup dialog.
712 This dialog is usually invoked when the user selects the Printer Setup
713 command.
714
715 DEVICE must be either an 'msprinter device, or a printer settings
716 object. The function brings up the Printer Setup dialog, where the user
717 can select a different printer and/or change printer options.
718 Connection name can change as a result of selecting a different printer
719 device. If a printer is specified, then changes are stored into the
720 settings object currently selected into that printer. If a settings
721 object is supplied, then changes are recorded into it, and, it it is
722 selected into a printer, then changes are propagated to that printer
723 too.
724
725 Return value is nil if the user has canceled the dialog. Otherwise, it
726 is a new plist, with the following properties:
727 name Printer device name, even if unchanged by the user.
728
729 The printer device is destroyed and an error is signaled if new printer
730 is selected by the user, but cannot be initialized.
731
732 See also `msprinter-print-dialog' and `msprinter-page-setup-dialog'.
733 */
734 (device))
735 {
736 return print_dialog_worker (device, 0);
737 }
738
739 DEFUN ("msprinter-print-dialog", Fmsprinter_print_dialog, 1, 1, 0, /*
740 Invoke Windows standard Print dialog.
741 This dialog is usually invoked when the user selects the Print command.
742 After the user presses OK, the program should start actual printout.
743
744 DEVICE must be either an 'msprinter device, or a printer settings
745 object. The function brings up the Print dialog, where the user can
746 select a different printer and/or change printer options. Connection
747 name can change as a result of selecting a different printer device. If
748 a printer is specified, then changes are stored into the settings object
749 currently selected into that printer. If a settings object is supplied,
750 then changes are recorded into it, and, it it is selected into a
751 printer, then changes are propagated to that printer
752 too.
753
754 Return value is nil if the user has canceled the dialog. Otherwise, it
755 is a new plist, with the following properties:
756 name Printer device name, even if unchanged by the user.
757 from-page First page to print, 1-based. If not specified by the user,
758 then this value is not included in the plist.
759 to-page Last page to print, inclusive, 1-based. If not specified by
760 the user, then this value is not included in the plist.
761 copies Number of copies to print. Always returned.
762
763 The DEVICE is destroyed and an error is signaled in case of
764 initialization problem with the new printer.
765
766 See also `msprinter-setup-print-dialog' and
767 `msprinter-page-setup-dialog'.
768 */
769 (device))
770 {
771 return print_dialog_worker (device, 1);
772 }
773
774
775 static int
776 plist_get_margin (Lisp_Object plist, Lisp_Object prop)
777 {
778 Lisp_Object val = Fplist_get (plist, prop, make_int (1440));
779 if (!INTP (val))
780 signal_simple_error ("Margin value must be an integer", val);
781
782 return MulDiv (XINT (val), 100, 144);
783 }
784
785 static Lisp_Object
786 plist_set_margin (Lisp_Object plist, Lisp_Object prop, int margin, int mm_p)
787 {
788 Lisp_Object val = make_int (MulDiv (margin, 144, mm_p ? 2450 : 100));
789 return Fcons (prop, Fcons (val, plist));
790 }
791
792 DEFUN ("msprinter-page-setup-dialog", Fmsprinter_page_setup_dialog, 1, 2, 0, /*
793 Invoke Windows standard Page Setup dialog.
794 This dialog is usually invoked in response to Page Setup command, and
795 used to chose such parameters as page orientation, print margins etc.
796 Note that this dialog contains the "Printer" button, which invokes
797 Printer Setup dialog (see `msprinter-print-setup-dialog') so that the
798 user can update the printer options or even select a different printer
799 as well.
800
801 DEVICE must be either an 'msprinter device, or a printer settings
802 object. The function brings up the Page Setup dialog, where the user
803 can select a different printer and/or change printer options.
804 Connection name can change as a result of selecting a different printer
805 device. If a printer is specified, then changes are stored into the
806 settings object currently selected into that printer. If a settings
807 object is supplied, then changes are recorded into it, and, it it is
808 selected into a printer, then changes are propagated to that printer
809 too.
810
811 PLIST is a plist of job properties;
812 see `default-msprinter-frame-plist' for the complete list. The plist
813 is used to initialize the dialog.
814
815 Return value is nil if the user has canceled the dialog. Otherwise,
816 it is a new plist, containing the new list of properties.
817
818 The DEVICE is destroyed and an error is signaled in case of
819 initialization problem with the new printer.
820
821 See also `msprinter-print-setup-dialog' and `msprinter-print-dialog'.
822 */
823 (device, plist))
824 {
825 Lisp_Devmode *ldm = decode_devmode (device);
826 PAGESETUPDLG pd;
827
828 memset (&pd, 0, sizeof (pd));
829 pd.lStructSize = sizeof (pd);
830 pd.hwndOwner = mswindows_get_selected_frame_hwnd ();
831 pd.Flags = PSD_MARGINS;
832 pd.rtMargin.left = plist_get_margin (plist, Qleft_margin);
833 pd.rtMargin.top = plist_get_margin (plist, Qtop_margin);
834 pd.rtMargin.right = plist_get_margin (plist, Qright_margin);
835 pd.rtMargin.bottom = plist_get_margin (plist, Qbottom_margin);
836 pd.hDevMode = devmode_to_hglobal (ldm);
837
838 if (!PageSetupDlg (&pd))
839 {
840 global_free_2_maybe (pd.hDevNames, pd.hDevMode);
841 return Qnil;
842 }
843
844 if (pd.hDevMode)
845 handle_devmode_changes (ldm, pd.hDevNames, pd.hDevMode);
846
847 /* Finally, build the resulting plist */
848 {
849 Lisp_Object result = Qnil;
850 int mm_p = pd.Flags & PSD_INHUNDREDTHSOFMILLIMETERS;
851 result = plist_set_margin (result, Qbottom_margin, pd.rtMargin.bottom, mm_p);
852 result = plist_set_margin (result, Qright_margin, pd.rtMargin.right, mm_p);
853 result = plist_set_margin (result, Qtop_margin, pd.rtMargin.top, mm_p);
854 result = plist_set_margin (result, Qleft_margin, pd.rtMargin.left, mm_p);
855 return result;
856 }
857 }
858
859 DEFUN ("msprinter-get-settings", Fmsprinter_get_settings, 1, 1, 0, /*
860 Return the settings object currently used by DEVICE.
861 The object returned is not a copy, but rather a pointer to the
862 original one. Use `msprinter-settings-copy' to create a copy of it.
863 */
864 (device))
865 {
866 struct device *d = decode_device (device);
867 XSETDEVICE (device, d);
868 CHECK_MSPRINTER_DEVICE (device);
869 return DEVICE_MSPRINTER_DEVMODE (d);
870 }
871
872 DEFUN ("msprinter-select-settings", Fmsprinter_select_settings, 2, 2, 0, /*
873 Select SETTINGS object into a DEVICE.
874 The settings from the settings object are immediately applied to the
875 printer, possibly changing even the target printer itself, and all
876 future changes are applied synchronously to the printer device and the
877 selected printer object, until a different settings object is selected
878 into the same printer.
879
880 A settings object can be selected to no more than one printer at a time.
881
882 If the supplied settings object is not specialized, it is specialized
883 for the printer immediately upon selection. The object can be
884 despecialized after it is unselected by calling the function
885 `msprinter-settings-despecialize'.
886
887 Return value is the previously selected settings object.
888 */
889 (device, settings))
890 {
891 Lisp_Devmode *ldm;
892 struct device *d = decode_device (device);
893
894 struct gcpro gcpro1;
895 GCPRO1 (settings);
896
897 XSETDEVICE (device, d);
898 CHECK_MSPRINTER_DEVICE (device);
899 CHECK_DEVMODE (settings);
900 ldm = XDEVMODE (settings);
901
902 if (!NILP (ldm->device))
903 signal_simple_error ("The object is currently selected into a device",
904 settings);
905
906 /* If the object being selected is de-specialized, then its
907 size is perhaps not enough to receive the new devmode. We can ask
908 for printer's devmode size here, because despecialized settings
909 cannot force switching to a different printer, as they supply no
910 printer name at all. */
911 if (ldm->printer_name == NULL)
912 {
913 size_t dm_size =
914 DocumentProperties (NULL, DEVICE_MSPRINTER_HPRINTER(d),
915 DEVICE_MSPRINTER_NAME(d), NULL, NULL, 0);
916 if (dm_size <= 0)
917 signal_simple_error ("Unable to specialize settings, printer error",
918 device);
919
920 assert (XDEVMODE_SIZE (ldm) <= dm_size);
921 ldm->devmode = xrealloc (ldm->devmode, dm_size);
922 }
923
924 /* If we bail out on signal here, no damage is done, except that
925 the stirage for the DEVMODE structure might be reallocated to
926 hold a larger one - not a big deal */
927 if (!sync_printer_with_devmode (d, ldm->devmode, ldm->devmode,
928 ldm->printer_name))
929 error ("Printer device initialization I/O error, device deleted.");
930
931 if (ldm->printer_name == NULL)
932 ldm->printer_name = xstrdup (DEVICE_MSPRINTER_NAME(d));
933
934 {
935 Lisp_Object old_mode = DEVICE_MSPRINTER_DEVMODE (d);
936 ldm->device = device;
937 XDEVMODE (old_mode)->device = Qnil;
938 DEVICE_MSPRINTER_DEVMODE (d) = settings;
939 UNGCPRO;
940 return old_mode;
941 }
942 }
943
944 DEFUN ("msprinter-apply-settings", Fmsprinter_apply_settings, 2, 2, 0, /*
945 Apply settings from a SETTINGS object to a 'msprinter DEVICE.
946 The settings from the settings object are immediately applied to the
947 printer, possibly changing even the target printer itself. The SETTING
948 object is not modified, unlike `msprinter-select-settings', and the
949 supplied object is not changed. The changes are immediately recorded
950 into the settings object which is currently selected into the printer
951 device.
952
953 Return value is the currently selected settings object.
954 */
955 (device, settings))
956 {
957 Lisp_Devmode *ldm_current, *ldm_new;
958 struct device *d = decode_device (device);
959
960 struct gcpro gcpro1;
961 GCPRO1 (settings);
962
963 XSETDEVICE (device, d);
964 CHECK_MSPRINTER_DEVICE (device);
965 CHECK_DEVMODE (settings);
966 ldm_new = XDEVMODE (settings);
967 ldm_current = XDEVMODE (DEVICE_MSPRINTER_DEVMODE (d));
968
969 /* If the supplied devmode is not specialized, then the current
970 devmode size will always be sufficient, as the printer does
971 not change. If it is specialized, we must reallocate the cuttent
972 devmode storage to match with the supplied one, as it has the right
973 size for the new printer, if it is going to change. The correct
974 way is to use the largest of the two though, to keep the old
975 contents unchanged in case of preliminary exit.
976 */
977 if (ldm_new->printer_name)
978 ldm_current->devmode =
979 (DEVMODE*) xrealloc (ldm_current->devmode,
980 max (XDEVMODE_SIZE (ldm_new),
981 XDEVMODE_SIZE (ldm_current)));
982
983 if (!sync_printer_with_devmode (d, ldm_new->devmode,
984 ldm_current->devmode,
985 ldm_new->printer_name))
986 error ("Printer device initialization I/O error, device deleted.");
987
988 if (ldm_new->printer_name != NULL)
989 {
990 xfree (ldm_current->printer_name);
991 ldm_current->printer_name = xstrdup (ldm_new->printer_name);
992 }
993
994 return DEVICE_MSPRINTER_DEVMODE (d);
995 }
996
997 /************************************************************************/
998 /* devmode */
999 /************************************************************************/
1000
1001 static void
1002 print_devmode (Lisp_Object obj, Lisp_Object printcharfun,
1003 int escapeflag)
1004 {
1005 char buf[100];
1006 Lisp_Devmode *dm = XDEVMODE (obj);
1007 if (print_readably)
1008 error ("printing unreadable object #<msprinter-settings 0x%x>",
1009 dm->header.uid);
1010 write_c_string ("#<msprinter-settings", printcharfun);
1011 if (dm->printer_name)
1012 {
1013 write_c_string (" for \"", printcharfun);
1014 write_c_string (dm->printer_name, printcharfun);
1015 write_c_string ("\"", printcharfun);
1016 }
1017 if (!NILP (dm->device))
1018 {
1019 write_c_string (" (currently on ", printcharfun);
1020 print_internal (dm->device, printcharfun, 0);
1021 write_c_string (")", printcharfun);
1022 }
1023 sprintf (buf, " 0x%x>", dm->header.uid);
1024 write_c_string (buf, printcharfun);
1025 }
1026
1027 static void
1028 finalize_devmode (void *header, int for_disksave)
1029 {
1030 Lisp_Devmode *dm = (Lisp_Devmode *) header;
1031
1032 if (for_disksave)
1033 {
1034 Lisp_Object devmode;
1035 XSETDEVMODE (devmode, dm);
1036 signal_simple_error (
1037 "Cannot dump XEmacs containing an msprinter-settings object",
1038 devmode);
1039 }
1040
1041 assert (NILP (dm->device));
1042
1043 if (dm->printer_name)
1044 xfree (dm->printer_name);
1045 }
1046
1047 static int
1048 equal_devmode (Lisp_Object obj1, Lisp_Object obj2, int depth)
1049 {
1050 Lisp_Devmode *dm1 = XDEVMODE (obj1);
1051 Lisp_Devmode *dm2 = XDEVMODE (obj2);
1052
1053 if ((dm1->devmode != NULL) != (dm1->devmode != NULL))
1054 return 0;
1055 if (dm1->devmode == NULL)
1056 return 1;
1057 if (memcmp (dm1->devmode, dm2->devmode, XDEVMODE_SIZE (dm1)) != 0)
1058 return 0;
1059 if (dm1->printer_name == NULL || dm2->printer_name == NULL)
1060 return 1;
1061 return stricmp (dm1->printer_name, dm2->printer_name) == 0;
1062 }
1063
1064 static unsigned long
1065 hash_devmode (Lisp_Object obj, int depth)
1066 {
1067 Lisp_Devmode *dm = XDEVMODE (obj);
1068
1069 return HASH3 (XDEVMODE_SIZE (dm),
1070 dm->devmode ? memory_hash (dm->devmode, XDEVMODE_SIZE (dm))
1071 : 0,
1072 dm->printer_name ? string_hash (dm->printer_name) : 0);
1073 }
1074
1075 DEFINE_LRECORD_IMPLEMENTATION ("msprinter-settings", devmode,
1076 0/*mark*/, print_devmode, finalize_devmode,
1077 equal_devmode, hash_devmode, 0/*description*/,
1078 Lisp_Devmode);
1079 static Lisp_Object
1080 allocate_devmode (DEVMODE* src_devmode, int do_copy,
1081 char* src_name, struct device *d)
1082 {
1083 Lisp_Devmode *dm;
1084 Lisp_Object ob;
1085
1086 dm = alloc_lcrecord_type (Lisp_Devmode, &lrecord_devmode);
1087
1088 if (d)
1089 XSETDEVICE (dm->device, d);
1090 else
1091 dm->device = Qnil;
1092
1093 dm->printer_name = src_name ? xstrdup (src_name) : NULL;
1094
1095 if (src_devmode != NULL && do_copy)
1096 {
1097 dm->devmode = (DEVMODE*) xmalloc (DEVMODE_SIZE (src_devmode));
1098 memcpy (dm->devmode, src_devmode, DEVMODE_SIZE (src_devmode));
1099 }
1100 else
1101 {
1102 dm->devmode = src_devmode;
1103 }
1104
1105 XSETDEVMODE (ob, dm);
1106 return ob;
1107 }
1108
1109 DEFUN ("msprinter-settings-copy", Fmsprinter_settings_copy, 1, 1, 0, /*
1110 Create and returns an exact copy of a printer settings object.
1111 */
1112 (settings))
1113 {
1114 Lisp_Devmode *dm;
1115
1116 CHECK_DEVMODE (settings);
1117 dm = XDEVMODE (settings);
1118
1119 return allocate_devmode (dm->devmode, 1, dm->printer_name, NULL);
1120 }
1121
1122 DEFUN ("msprinter-settings-despecialize", Fmsprinter_settings_despecialize, 1, 1, 0, /*
1123 Erase printer-specific settings from a printer settings object.
1124 */
1125 (settings))
1126 {
1127 Lisp_Devmode *ldm;
1128 DEVMODE *dm;
1129
1130 CHECK_DEVMODE (settings);
1131 ldm = XDEVMODE (settings);
1132
1133 if (!NILP (ldm->device))
1134 signal_simple_error ("The object is currently selected into a device",
1135 settings);
1136
1137 dm = ldm->devmode;
1138
1139 /* #### TODO. Either remove references to device specific bins,
1140 paper sizes etc, or signal an error of they are present. */
1141
1142 dm->dmDriverExtra = 0;
1143 dm->dmDeviceName[0] = '\0';
1144
1145 if (ldm->printer_name)
1146 xfree (ldm->printer_name);
1147
1148 return Qnil;
526 } 1149 }
527 1150
528 1151
529 /************************************************************************/ 1152 /************************************************************************/
530 /* initialization */ 1153 /* initialization */
531 /************************************************************************/ 1154 /************************************************************************/
532 1155
533 void 1156 void
534 syms_of_device_mswindows (void) 1157 syms_of_device_mswindows (void)
535 { 1158 {
1159 INIT_LRECORD_IMPLEMENTATION (devmode);
1160
1161 DEFSUBR (Fmsprinter_print_setup_dialog);
1162 DEFSUBR (Fmsprinter_print_dialog);
1163 DEFSUBR (Fmsprinter_page_setup_dialog);
1164 DEFSUBR (Fmsprinter_get_settings);
1165 DEFSUBR (Fmsprinter_select_settings);
1166 DEFSUBR (Fmsprinter_apply_settings);
1167 DEFSUBR (Fmsprinter_settings_copy);
1168 DEFSUBR (Fmsprinter_settings_despecialize);
1169
536 defsymbol (&Qinit_pre_mswindows_win, "init-pre-mswindows-win"); 1170 defsymbol (&Qinit_pre_mswindows_win, "init-pre-mswindows-win");
537 defsymbol (&Qinit_post_mswindows_win, "init-post-mswindows-win"); 1171 defsymbol (&Qinit_post_mswindows_win, "init-post-mswindows-win");
538 } 1172 }
539 1173
540 void 1174 void