2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * PURPOSE: Monitor support
22 * FILE: subsys/win32k/ntuser/monitor.c
23 * PROGRAMER: Anich Gregor (blight@blight.eu.org)
28 /* INCLUDES ******************************************************************/
32 /* FIXME: find include file for these */
33 #define MONITORINFOF_PRIMARY 1
34 #define MONITOR_DEFAULTTONULL 0
35 #define MONITOR_DEFAULTTOPRIMARY 1
36 #define MONITOR_DEFAULTTONEAREST 2
41 /* GLOBALS *******************************************************************/
43 /* list of monitors */
44 static PMONITOR_OBJECT gMonitorList
= NULL
;
46 /* INITALIZATION FUNCTIONS ****************************************************/
51 DPRINT("Initializing monitor implementation...\n");
53 return STATUS_SUCCESS
;
59 DPRINT("Cleaning up monitor implementation...\n");
60 /* FIXME: Destroy monitor objects? */
62 return STATUS_SUCCESS
;
65 /* PRIVATE FUNCTIONS **********************************************************/
68 # define MIN(a, b) ((a) < (b) ? (a) : (b))
71 # define MAX(a, b) ((a) > (b) ? (a) : (b))
74 # define ABS(a) ((a) < (0) ? (-(a)) : (a))
77 /* IntCreateMonitorObject
79 * Creates a MONITOR_OBJECT
82 * If the function succeeds a pointer to a MONITOR_OBJECT is returned. On failure
87 IntCreateMonitorObject()
90 PMONITOR_OBJECT Monitor
;
92 Monitor
= ObmCreateObject(&gHandleTable
, &Handle
, otMonitor
, sizeof (MONITOR_OBJECT
));
98 Monitor
->Handle
= Handle
;
99 ExInitializeFastMutex(&Monitor
->Lock
);
104 /* IntDestroyMonitorObject
106 * Destroys a MONITOR_OBJECT
107 * You have to be the owner of the monitors lock to safely destroy it.
112 * Pointer to the MONITOR_OBJECT which shall be deleted
116 IntDestroyMonitorObject(IN PMONITOR_OBJECT pMonitor
)
118 RtlFreeUnicodeString(&pMonitor
->DeviceName
);
119 ObmDereferenceObject(pMonitor
);
124 PMONITOR_OBJECT FASTCALL
125 UserGetMonitorObject(IN HMONITOR hMonitor
)
127 PMONITOR_OBJECT Monitor
;
131 SetLastWin32Error(ERROR_INVALID_MONITOR_HANDLE
);
136 Monitor
= (PMONITOR_OBJECT
)UserGetObject(&gHandleTable
, hMonitor
, otMonitor
);
139 SetLastWin32Error(ERROR_INVALID_MONITOR_HANDLE
);
143 ASSERT(USER_BODY_TO_HEADER(Monitor
)->RefCount
>= 0);
151 * Creates a new MONITOR_OBJECT and appends it to the list of monitors.
155 * pGdiDevice Pointer to the GDIDEVICE onto which the monitor was attached
156 * DisplayNumber Display Number (starting with 0)
162 IntAttachMonitor(IN GDIDEVICE
*pGdiDevice
,
163 IN ULONG DisplayNumber
)
165 PMONITOR_OBJECT Monitor
;
166 WCHAR Buffer
[CCHDEVICENAME
];
168 DPRINT("Attaching monitor...\n");
170 /* create new monitor object */
171 Monitor
= IntCreateMonitorObject();
174 DPRINT("Couldnt create monitor object\n");
175 return STATUS_INSUFFICIENT_RESOURCES
;
178 _snwprintf(Buffer
, CCHDEVICENAME
, L
"\\??\\DISPLAY%d", DisplayNumber
+ 1);
179 if (!RtlCreateUnicodeString(&Monitor
->DeviceName
, Buffer
))
181 DPRINT("Couldn't duplicate monitor name!\n");
182 return STATUS_INSUFFICIENT_RESOURCES
;
185 Monitor
->GdiDevice
= pGdiDevice
;
186 if (gMonitorList
== NULL
)
188 DPRINT("Primary monitor is beeing attached\n");
189 Monitor
->IsPrimary
= TRUE
;
190 gMonitorList
= Monitor
;
195 DPRINT("Additional monitor is beeing attached\n");
196 for (p
= gMonitorList
; p
->Next
!= NULL
; p
= p
->Next
)
204 return STATUS_SUCCESS
;
209 * Deletes a MONITOR_OBJECT and removes it from the list of monitors.
213 * pGdiDevice Pointer to the GDIDEVICE from which the monitor was detached
219 IntDetachMonitor(IN GDIDEVICE
*pGdiDevice
)
221 PMONITOR_OBJECT Monitor
;
223 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
225 if (Monitor
->GdiDevice
== pGdiDevice
)
231 /* no monitor for given device found */
232 return STATUS_INVALID_PARAMETER
;
235 if (Monitor
->IsPrimary
&& (Monitor
->Next
!= NULL
|| Monitor
->Prev
!= NULL
))
237 PMONITOR_OBJECT NewPrimaryMonitor
= (Monitor
->Prev
!= NULL
) ? (Monitor
->Prev
) : (Monitor
->Next
);
239 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&NewPrimaryMonitor
->Lock
);
240 NewPrimaryMonitor
->IsPrimary
= TRUE
;
241 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&NewPrimaryMonitor
->Lock
);
244 if (gMonitorList
== Monitor
)
246 gMonitorList
= Monitor
->Next
;
247 if (Monitor
->Next
!= NULL
)
248 Monitor
->Next
->Prev
= NULL
;
252 Monitor
->Prev
->Next
= Monitor
->Next
;
253 if (Monitor
->Next
!= NULL
)
254 Monitor
->Next
->Prev
= Monitor
->Prev
;
257 IntDestroyMonitorObject(Monitor
);
259 return STATUS_SUCCESS
;
262 /* IntGetPrimaryMonitor
264 * Returns a PMONITOR_OBJECT for the primary monitor
271 IntGetPrimaryMonitor()
273 PMONITOR_OBJECT Monitor
;
275 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
277 /* FIXME: I guess locking the monitor is not neccessary to read 1 int */
278 if (Monitor
->IsPrimary
)
285 /* IntGetMonitorsFromRect
287 * Returns a list of monitor handles/rectangles. The rectangles in the list are
288 * the areas of intersection with the monitors.
293 * Rectangle in desktop coordinates. If this is NULL all monitors are
294 * returned and the rect list is filled with the sizes of the monitors.
297 * Pointer to an array of HMONITOR which is filled with monitor handles.
301 * Pointer to an array of RECT which is filled with intersection rects in
302 * desktop coordinates.
303 * Can be NULL, will be ignored if no intersecting monitor is found and
304 * flags is MONITOR_DEFAULTTONEAREST
307 * Size of the hMonitorList and monitorRectList arguments. If this is zero
308 * hMonitorList and monitorRectList are ignored.
311 * Either 0 or MONITOR_DEFAULTTONEAREST (ignored if rect is NULL)
314 * The number of monitors which intersect the specified region.
318 IntGetMonitorsFromRect(OPTIONAL IN LPCRECT pRect
,
319 OPTIONAL OUT HMONITOR
*hMonitorList
,
320 OPTIONAL OUT LPRECT monitorRectList
,
321 OPTIONAL IN DWORD listSize
,
322 OPTIONAL IN DWORD flags
)
324 PMONITOR_OBJECT Monitor
, NearestMonitor
= NULL
;
326 LONG iNearestDistanceX
= 0x7fffffff, iNearestDistanceY
= 0x7fffffff;
328 /* find monitors which intersect the rectangle */
329 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
331 RECT MonitorRect
, IntersectionRect
;
333 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Monitor
->Lock
);
334 MonitorRect
.left
= 0; /* FIXME: get origin */
335 MonitorRect
.top
= 0; /* FIXME: get origin */
336 MonitorRect
.right
= MonitorRect
.left
+ Monitor
->GdiDevice
->GDIInfo
.ulHorzRes
;
337 MonitorRect
.bottom
= MonitorRect
.top
+ Monitor
->GdiDevice
->GDIInfo
.ulVertRes
;
338 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Monitor
->Lock
);
340 DPRINT("MonitorRect: left = %d, top = %d, right = %d, bottom = %d\n",
341 MonitorRect
.left
, MonitorRect
.top
, MonitorRect
.right
, MonitorRect
.bottom
);
345 BOOL intersects
= TRUE
;
347 /* check if the rect intersects the monitor */
348 if ((pRect
->right
< MonitorRect
.left
) || (pRect
->left
> MonitorRect
.right
) ||
349 (pRect
->bottom
< MonitorRect
.top
) || (pRect
->top
> MonitorRect
.bottom
))
354 if (flags
== MONITOR_DEFAULTTONEAREST
&& !intersects
)
356 INT distanceX
, distanceY
;
358 distanceX
= MIN(ABS(MonitorRect
.left
- pRect
->right
),
359 ABS(pRect
->left
- MonitorRect
.right
));
360 distanceY
= MIN(ABS(MonitorRect
.top
- pRect
->bottom
),
361 ABS(pRect
->top
- MonitorRect
.bottom
));
363 if (((distanceX
< iNearestDistanceX
) && (distanceY
<= iNearestDistanceY
)) ||
364 ((distanceX
<= iNearestDistanceX
) && (distanceY
< iNearestDistanceY
)))
366 iNearestDistanceX
= distanceX
;
367 iNearestDistanceY
= distanceY
;
368 NearestMonitor
= Monitor
;
375 /* calculate intersection */
376 IntersectionRect
.left
= MAX(MonitorRect
.left
, pRect
->left
);
377 IntersectionRect
.top
= MAX(MonitorRect
.top
, pRect
->top
);
378 IntersectionRect
.right
= MIN(MonitorRect
.right
, pRect
->right
);
379 IntersectionRect
.bottom
= MIN(MonitorRect
.bottom
, pRect
->bottom
);
383 IntersectionRect
= MonitorRect
;
386 if (iCount
< listSize
)
388 if (hMonitorList
!= NULL
)
389 hMonitorList
[iCount
] = Monitor
->Handle
;
390 if (monitorRectList
!= NULL
)
391 monitorRectList
[iCount
] = IntersectionRect
;
396 if (iCount
== 0 && flags
== MONITOR_DEFAULTTONEAREST
)
398 if (iCount
< listSize
)
400 if (hMonitorList
!= NULL
)
401 hMonitorList
[iCount
] = NearestMonitor
->Handle
;
409 /* PUBLIC FUNCTIONS ***********************************************************/
411 /* NtUserEnumDisplayMonitors
413 * Enumerates display monitors which intersect the given HDC/cliprect
418 * Handle to a DC for which to enum intersecting monitors. If this is NULL
419 * it returns all monitors which are part of the current virtual screen.
422 * Clipping rectangle with coordinate system origin at the DCs origin if the
423 * given HDC is not NULL or in virtual screen coordinated if it is NULL.
427 * Pointer to an array of HMONITOR which is filled with monitor handles.
431 * Pointer to an array of RECT which is filled with intersection rectangles.
435 * Size of the hMonitorList and monitorRectList arguments. If this is zero
436 * hMonitorList and monitorRectList are ignored.
439 * The number of monitors which intersect the specified region or -1 on failure.
443 NtUserEnumDisplayMonitors(
445 OPTIONAL IN LPCRECT pRect
,
446 OPTIONAL OUT HMONITOR
*hMonitorList
,
447 OPTIONAL OUT LPRECT monitorRectList
,
448 OPTIONAL IN DWORD listSize
)
451 HMONITOR
*safeHMonitorList
= NULL
;
452 LPRECT safeRectList
= NULL
;
460 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
461 if (!NT_SUCCESS(status
))
463 DPRINT("MmCopyFromCaller() failed!\n");
464 SetLastNtError(status
);
475 /* get visible region bounding rect */
479 DPRINT("DC_LockDc() failed!\n");
480 /* FIXME: setlasterror? */
483 dcVisRgn
= dc
->w
.hVisRgn
;
486 regionType
= NtGdiGetRgnBox(dcVisRgn
, &dcRect
);
489 DPRINT("NtGdiGetRgnBox() failed!\n");
492 if (regionType
== NULLREGION
)
494 if (regionType
== COMPLEXREGION
)
495 { /* TODO: warning */
498 /* if hDC and pRect are given the area of interest is pRect with
499 coordinate origin at the DC position */
502 rect
.left
+= dcRect
.left
;
503 rect
.right
+= dcRect
.left
;
504 rect
.top
+= dcRect
.top
;
505 rect
.bottom
+= dcRect
.top
;
507 /* if hDC is given and pRect is not the area of interest is the
508 bounding rect of hDC */
515 if (hDC
== NULL
&& pRect
== NULL
)
520 /* find intersecting monitors */
521 numMonitors
= IntGetMonitorsFromRect(myRect
, NULL
, NULL
, 0, 0);
522 if (numMonitors
== 0 || listSize
== 0 ||
523 (hMonitorList
== NULL
&& monitorRectList
== NULL
))
525 DPRINT("numMonitors = %d\n", numMonitors
);
529 if (hMonitorList
!= NULL
&& listSize
!= 0)
531 safeHMonitorList
= ExAllocatePool(PagedPool
, sizeof (HMONITOR
) * listSize
);
532 if (safeHMonitorList
== NULL
)
534 /* FIXME: SetLastWin32Error? */
538 if (monitorRectList
!= NULL
&& listSize
!= 0)
540 safeRectList
= ExAllocatePool(PagedPool
, sizeof (RECT
) * listSize
);
541 if (safeRectList
== NULL
)
543 ExFreePool(safeHMonitorList
);
544 /* FIXME: SetLastWin32Error? */
549 /* get intersecting monitors */
550 numMonitors
= IntGetMonitorsFromRect(myRect
, safeHMonitorList
, safeRectList
,
553 if (hDC
!= NULL
&& pRect
!= NULL
&& safeRectList
!= NULL
)
554 for (i
= 0; i
< numMonitors
; i
++)
556 safeRectList
[i
].left
-= dcRect
.left
;
557 safeRectList
[i
].right
-= dcRect
.left
;
558 safeRectList
[i
].top
-= dcRect
.top
;
559 safeRectList
[i
].bottom
-= dcRect
.top
;
563 if (hMonitorList
!= NULL
&& listSize
!= 0)
565 status
= MmCopyToCaller(hMonitorList
, safeHMonitorList
, sizeof (HMONITOR
) * listSize
);
566 ExFreePool(safeHMonitorList
);
567 if (!NT_SUCCESS(status
))
569 ExFreePool(safeRectList
);
570 SetLastNtError(status
);
574 if (monitorRectList
!= NULL
&& listSize
!= 0)
576 status
= MmCopyToCaller(monitorRectList
, safeRectList
, sizeof (RECT
) * listSize
);
577 ExFreePool(safeRectList
);
578 if (!NT_SUCCESS(status
))
580 SetLastNtError(status
);
588 /* NtUserGetMonitorInfo
590 * Retrieves information about a given monitor
595 * Handle to a monitor for which to get information
598 * Pointer to a MONITORINFO struct which is filled with the information.
599 * The cbSize member must be set to sizeof(MONITORINFO) or
600 * sizeof(MONITORINFOEX). Even if set to sizeof(MONITORINFOEX) only parts
601 * from MONITORINFO will be filled.
604 * Pointer to a UNICODE_STRING which will recieve the device's name. The
605 * length should be CCHDEVICENAME
609 * TRUE on success; FALSE on failure (calls SetLastNtError())
614 NtUserGetMonitorInfo(
615 IN HMONITOR hMonitor
,
616 OUT LPMONITORINFO pMonitorInfo
)
618 PMONITOR_OBJECT Monitor
;
619 MONITORINFOEXW MonitorInfo
;
621 DECLARE_RETURN(BOOL
);
623 DPRINT("Enter NtUserGetMonitorInfo\n");
626 /* get monitor object */
627 if (!(Monitor
= UserGetMonitorObject(hMonitor
)))
629 DPRINT("Couldnt find monitor 0x%lx\n", hMonitor
);
633 if(pMonitorInfo
== NULL
)
635 SetLastNtError(STATUS_INVALID_PARAMETER
);
639 /* get size of pMonitorInfo */
640 Status
= MmCopyFromCaller(&MonitorInfo
.cbSize
, &pMonitorInfo
->cbSize
, sizeof (MonitorInfo
.cbSize
));
641 if (!NT_SUCCESS(Status
))
643 SetLastNtError(Status
);
646 if ((MonitorInfo
.cbSize
!= sizeof (MONITORINFO
)) &&
647 (MonitorInfo
.cbSize
!= sizeof (MONITORINFOEXW
)))
649 SetLastNtError(STATUS_INVALID_PARAMETER
);
653 /* fill monitor info */
654 MonitorInfo
.rcMonitor
.left
= 0; /* FIXME: get origin */
655 MonitorInfo
.rcMonitor
.top
= 0; /* FIXME: get origin */
656 MonitorInfo
.rcMonitor
.right
= MonitorInfo
.rcMonitor
.left
+ Monitor
->GdiDevice
->GDIInfo
.ulHorzRes
;
657 MonitorInfo
.rcMonitor
.bottom
= MonitorInfo
.rcMonitor
.top
+ Monitor
->GdiDevice
->GDIInfo
.ulVertRes
;
658 MonitorInfo
.rcWork
= MonitorInfo
.rcMonitor
; /* FIXME: use DEVMODE panning to calculate work area? */
659 MonitorInfo
.dwFlags
= 0;
661 if (Monitor
->IsPrimary
)
662 MonitorInfo
.dwFlags
|= MONITORINFOF_PRIMARY
;
664 /* fill device name */
665 if (MonitorInfo
.cbSize
== sizeof (MONITORINFOEXW
))
668 INT len
= Monitor
->DeviceName
.Length
;
669 if (len
>= CCHDEVICENAME
* sizeof (WCHAR
))
670 len
= (CCHDEVICENAME
- 1) * sizeof (WCHAR
);
672 memcpy(MonitorInfo
.szDevice
, Monitor
->DeviceName
.Buffer
, len
);
673 memcpy(MonitorInfo
.szDevice
+ (len
/ sizeof (WCHAR
)), &nul
, sizeof (WCHAR
));
677 Status
= MmCopyToCaller(pMonitorInfo
, &MonitorInfo
, MonitorInfo
.cbSize
);
678 if (!NT_SUCCESS(Status
))
680 DPRINT("GetMonitorInfo: MmCopyToCaller failed\n");
681 SetLastNtError(Status
);
685 DPRINT("GetMonitorInfo: success\n");
690 DPRINT("Leave NtUserGetMonitorInfo, ret=%i\n",_ret_
);
695 /* NtUserMonitorFromPoint
697 * Returns a handle to the monitor containing the given point.
702 * Point for which to find monitor
705 * Specifies the behaviour if the point isn't on any of the monitors.
708 * If the point is found a handle to the monitor is returned; if not the
709 * return value depends on dwFlags
713 NtUserMonitorFromPoint(
719 HMONITOR hMonitor
= NULL
;
722 InRect
.left
= InRect
.right
= point
.x
;
723 InRect
.top
= InRect
.bottom
= point
.y
;
725 /* find intersecting monitor */
726 NumMonitors
= IntGetMonitorsFromRect(&InRect
, &hMonitor
, NULL
, 1, 0);
729 return (HMONITOR
)NULL
;
732 if (hMonitor
== NULL
)
734 if (dwFlags
== MONITOR_DEFAULTTOPRIMARY
)
736 PMONITOR_OBJECT MonitorObj
= IntGetPrimaryMonitor();
738 hMonitor
= MonitorObj
->Handle
;
740 else if (dwFlags
== MONITOR_DEFAULTTONEAREST
)
742 NumMonitors
= IntGetMonitorsFromRect(&InRect
, &hMonitor
, NULL
,
743 1, MONITOR_DEFAULTTONEAREST
);
744 /*ASSERT( (numMonitors > 0) && (hMonitor != NULL) );*/
746 /* else flag is DEFAULTTONULL */
752 /* NtUserMonitorFromRect
754 * Returns a handle to the monitor having the largest intersection with a
760 * Pointer to a RECT for which to find monitor
763 * Specifies the behaviour if no monitor intersects the given rect
766 * If a monitor intersects the rect a handle to it is returned; if not the
767 * return value depends on dwFlags
771 NtUserMonitorFromRect(
775 INT numMonitors
, iLargestArea
= -1, i
;
777 HMONITOR
*hMonitorList
;
778 HMONITOR hMonitor
= NULL
;
783 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
784 if (!NT_SUCCESS(status
))
786 SetLastNtError(status
);
787 return (HMONITOR
)NULL
;
790 /* find intersecting monitors */
791 numMonitors
= IntGetMonitorsFromRect(&rect
, NULL
, NULL
, 0, 0);
794 return (HMONITOR
)NULL
;
797 if (numMonitors
== 0)
799 if (dwFlags
== MONITOR_DEFAULTTOPRIMARY
)
801 PMONITOR_OBJECT monitorObj
= IntGetPrimaryMonitor();
803 return monitorObj
->Handle
;
805 else if (dwFlags
== MONITOR_DEFAULTTONEAREST
)
807 numMonitors
= IntGetMonitorsFromRect(&rect
, &hMonitor
, NULL
,
808 1, MONITOR_DEFAULTTONEAREST
);
809 if (numMonitors
<= 0)
812 return (HMONITOR
)NULL
;
818 /* else flag is DEFAULTTONULL */
819 return (HMONITOR
)NULL
;
822 hMonitorList
= ExAllocatePool(PagedPool
, sizeof (HMONITOR
) * numMonitors
);
823 if (hMonitorList
== NULL
)
825 /* FIXME: SetLastWin32Error? */
826 return (HMONITOR
)NULL
;
828 rectList
= ExAllocatePool(PagedPool
, sizeof (RECT
) * numMonitors
);
829 if (rectList
== NULL
)
831 ExFreePool(hMonitorList
);
832 /* FIXME: SetLastWin32Error? */
833 return (HMONITOR
)NULL
;
836 /* get intersecting monitors */
837 numMonitors
= IntGetMonitorsFromRect(&rect
, hMonitorList
, rectList
,
839 if (numMonitors
<= 0)
841 return (HMONITOR
)NULL
;
844 /* find largest intersection */
845 for (i
= 0; i
< numMonitors
; i
++)
847 INT area
= (rectList
[i
].right
- rectList
[i
].left
) *
848 (rectList
[i
].bottom
- rectList
[i
].top
);
849 if (area
> iLargestArea
)
851 hMonitor
= hMonitorList
[i
];
855 ExFreePool(hMonitorList
);
856 ExFreePool(rectList
);
864 NtUserMonitorFromWindow(
868 PWINDOW_OBJECT Window
;
869 HMONITOR hMonitor
= NULL
;
871 DECLARE_RETURN(HMONITOR
);
873 DPRINT("Enter NtUserMonitorFromWindow\n");
876 if (!(Window
= UserGetWindowObject(hWnd
)))
881 Rect
.left
= Rect
.right
= Window
->WindowRect
.left
;
882 Rect
.top
= Rect
.bottom
= Window
->WindowRect
.bottom
;
884 IntGetMonitorsFromRect(&Rect
, &hMonitor
, NULL
, 1, dwFlags
);
889 DPRINT("Leave NtUserMonitorFromWindow, ret=%i\n",_ret_
);