2 Copyright © 1995-2017, The AROS Development Team. All rights reserved.
9 /****************************************************************************************/
11 #include "gfx_debug.h"
13 #include <proto/alib.h>
14 #include <proto/graphics.h>
15 #include <proto/oop.h>
16 #include <proto/utility.h>
17 #include <exec/memory.h>
18 #include <graphics/gfxnodes.h>
20 #include <utility/tagitem.h>
25 #include "gfx_intern.h"
28 #define GfxBase csd->cs_GfxBase
30 /****************************************************************************************/
32 static BOOL
parse_sync_tags(struct class_static_data
*csd
, struct sync_data
*data
, struct TagItem
*tags
, BOOL init
);
34 /****************************************************************************************/
36 OOP_Object
*Sync__Root__New(OOP_Class
*cl
, OOP_Object
*o
, struct pRoot_New
*msg
)
38 struct class_static_data
*csd
= CSD(cl
);
39 struct Library
*UtilityBase
= csd
->cs_UtilityBase
;
40 struct Library
*OOPBase
= csd
->cs_OOPBase
;
43 EnterFunc(bug("Sync::New()\n"));
46 * We need graphics.library in order to be able to create MonitorSpec.
47 * we do it here because gfx.hidd is initialized before
50 ObtainSemaphore(&csd
->sema
);
54 GfxBase
= (void *)OpenLibrary("graphics.library", 41);
58 ReleaseSemaphore(&csd
->sema
);
63 /* Get object from superclass */
64 o
= (OOP_Object
*)OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
67 struct sync_data
*data
= OOP_INST_DATA(cl
, o
);
68 struct TagItem
*tstate
= msg
->attrList
;
73 /* Parse mandatory attributes */
74 while ((tag
= NextTagItem(&tstate
)))
78 if (IS_SYNC_ATTR(tag
->ti_Tag
, idx
))
82 case aoHidd_Sync_HDisp
:
83 data
->hdisp
= tag
->ti_Data
;
86 case aoHidd_Sync_VDisp
:
87 data
->vdisp
= tag
->ti_Data
;
90 case aoHidd_Sync_Flags
:
91 data
->flags
= tag
->ti_Data
;
94 case aoHidd_Sync_Description
:
95 s
= (char *)tag
->ti_Data
;
98 case aoHidd_Sync_BoardNumber
:
102 case aoHidd_Sync_Variable
:
104 data
->InternalFlags
|= SYNC_VARIABLE
;
107 case aoHidd_Sync_MonitorSpec
:
108 data
->mspc
= (struct MonitorSpec
*)tag
->ti_Data
;
111 case aoHidd_Sync_GfxHidd
:
112 data
->gfxhidd
= (OOP_Object
*)tag
->ti_Data
;
118 /* We must have HDisp, VDisp and GfxHidd */
119 if ((!data
->hdisp
) || (!data
->vdisp
) || (!data
->gfxhidd
))
122 if (ok
&& (!data
->mspc
))
125 * We must have a MonitorSpec. Either it's pre-cooked by the driver
126 * (useful for Amiga(tm) chipset), or we create it ourselves
128 data
->mspc
= (struct MonitorSpec
*)GfxNew(MONITOR_SPEC_TYPE
);
131 data
->mspc
->ms_Node
.xln_Name
= data
->description
;
132 data
->mspc
->ratioh
= RATIO_UNITY
;
133 data
->mspc
->ratiov
= RATIO_UNITY
;
134 InitSemaphore(&data
->mspc
->DisplayInfoDataBaseSemaphore
);
136 data
->InternalFlags
|= SYNC_FREE_MONITORSPEC
;
144 /* By default minimum/maximum bitmap size is equal to display size */
145 data
->hmin
= data
->hdisp
;
146 data
->vmin
= data
->vdisp
;
148 if (GFXHIDD__Hidd_Gfx__GetFBModeQuick(csd
->gfxhiddclass
, data
->gfxhidd
) == vHidd_FrameBuffer_Mirrored
)
150 /* But for mirrored framebuffer mode we can have larger bitmaps */
156 data
->hmax
= data
->hdisp
;
157 data
->vmax
= data
->vdisp
;
160 /* Now try to override defaults */
161 tstate
= msg
->attrList
;
162 while ((tag
= NextTagItem(&tstate
)))
166 Hidd_Sync_Switch(tag
->ti_Tag
, idx
)
168 case aoHidd_Sync_HMin
:
169 data
->hmin
= tag
->ti_Data
;
172 case aoHidd_Sync_HMax
:
173 data
->hmax
= tag
->ti_Data
;
176 case aoHidd_Sync_VMin
:
177 data
->vmin
= tag
->ti_Data
;
180 case aoHidd_Sync_VMax
:
181 data
->vmax
= tag
->ti_Data
;
186 /* Format description */
189 char *d
= data
->description
;
190 int dlen
= sizeof(data
->description
);
199 /* It's a format prefix, let's deal with it */
204 l
= snprintf(d
, dlen
, "%u", (unsigned)board
);
208 l
= snprintf(d
, dlen
, "%u", (unsigned)data
->hdisp
);
212 l
= snprintf(d
, dlen
, "%u", (unsigned)data
->vdisp
);
216 /* Just copy over two chars */
219 /* Copy next character only if we have room for it */
230 /* Copy one character */
235 /* If NULL byte has been just transferred, exit, the string is already terminated */
239 /* Increment pointer, decrement length */
243 /* If we have only one byte in the destination left, terminate the string and exit */
252 ok
= parse_sync_tags(CSD(cl
), data
, msg
->attrList
, TRUE
);
257 /* Set object pointer and add the MonitorSpec to the GfxBase->MonitorList */
258 data
->mspc
->ms_Object
= (void *)o
;
260 ObtainSemaphore(GfxBase
->MonitorListSemaphore
);
261 Enqueue(&GfxBase
->MonitorList
, (struct Node
*)data
->mspc
);
262 ReleaseSemaphore(GfxBase
->MonitorListSemaphore
);
268 OOP_MethodID dispose_mid
;
270 D(bug("!!! ERROR PARSING SYNC ATTRS IN Sync::New() !!!\n"));
272 dispose_mid
= OOP_GetMethodID(IID_Root
, moRoot_Dispose
);
273 OOP_CoerceMethod(cl
, o
, &dispose_mid
);
280 /****************************************************************************************/
282 VOID
Sync__Root__Dispose(OOP_Class
*cl
, OOP_Object
*o
, OOP_Msg msg
)
284 struct sync_data
*data
= OOP_INST_DATA(cl
, o
);
285 struct class_static_data
*csd
= CSD(cl
);
287 /* First we remove the MonitorSpec from the list, if it's our MonitorSpec
288 and it's in the list */
289 if ((data
->InternalFlags
& SYNC_FREE_MONITORSPEC
) &&
290 data
->mspc
->ms_Node
.xln_Succ
) {
292 ObtainSemaphore(GfxBase
->MonitorListSemaphore
);
293 Remove((struct Node
*)data
->mspc
);
294 ReleaseSemaphore(GfxBase
->MonitorListSemaphore
);
297 /* Then we dispose things that we created */
298 if (data
->InternalFlags
& SYNC_FREE_SPECIALMONITOR
)
299 GfxFree(&data
->mspc
->ms_Special
->spm_Node
);
300 if (data
->InternalFlags
& SYNC_FREE_MONITORSPEC
)
301 GfxFree(&data
->mspc
->ms_Node
);
303 OOP_DoSuperMethod(cl
, o
, msg
);
306 /****************************************************************************************/
308 VOID
Sync__Root__Get(OOP_Class
*cl
, OOP_Object
*o
, struct pRoot_Get
*msg
)
310 struct sync_data
*data
;
311 struct class_static_data
*csd
;
314 data
= OOP_INST_DATA(cl
, o
);
317 if (IS_SYNC_ATTR(msg
->attrID
, idx
))
319 UWORD hsync_start
, hsync_end
, vsync_start
, vsync_end
;
321 if (data
->mspc
->ms_Special
) {
322 hsync_start
= data
->mspc
->ms_Special
->hsync
.asi_Start
;
323 hsync_end
= data
->mspc
->ms_Special
->hsync
.asi_Stop
;
324 vsync_start
= data
->mspc
->ms_Special
->vsync
.asi_Start
;
325 vsync_end
= data
->mspc
->ms_Special
->vsync
.asi_Stop
;
327 /* Special failback values that will result in zero margins and sync lengths */
328 hsync_start
= data
->hdisp
;
329 hsync_end
= data
->hdisp
;
330 vsync_start
= data
->vdisp
;
331 vsync_end
= data
->vdisp
;
336 case aoHidd_Sync_PixelTime
:
337 if (data
->pixelclock
) {
338 ULONG khz
= data
->pixelclock
/ 1000;
340 *msg
->storage
= 1000000000 / khz
;
344 case aoHidd_Sync_PixelClock
:
345 *msg
->storage
= data
->pixelclock
;
348 case aoHidd_Sync_LeftMargin
:
349 *msg
->storage
= data
->htotal
- hsync_end
;
352 case aoHidd_Sync_RightMargin
:
353 *msg
->storage
= hsync_start
- data
->hdisp
;
356 case aoHidd_Sync_HSyncLength
:
357 *msg
->storage
= hsync_end
- hsync_start
;
360 case aoHidd_Sync_UpperMargin
:
361 *msg
->storage
= data
->mspc
->total_rows
- vsync_end
;
364 case aoHidd_Sync_LowerMargin
:
365 *msg
->storage
= vsync_end
- data
->vdisp
;
368 case aoHidd_Sync_VSyncLength
:
369 *msg
->storage
= vsync_end
- vsync_start
;
372 case aoHidd_Sync_HDisp
:
373 *msg
->storage
= data
->hdisp
;
376 case aoHidd_Sync_VDisp
:
377 *msg
->storage
= data
->vdisp
;
380 case aoHidd_Sync_HSyncStart
:
381 *msg
->storage
= hsync_start
;
384 case aoHidd_Sync_HSyncEnd
:
385 *msg
->storage
= hsync_end
;
388 case aoHidd_Sync_HTotal
:
389 *msg
->storage
= data
->htotal
;
392 case aoHidd_Sync_VSyncStart
:
393 *msg
->storage
= vsync_start
;
396 case aoHidd_Sync_VSyncEnd
:
397 *msg
->storage
= vsync_end
;
400 case aoHidd_Sync_VTotal
:
401 *msg
->storage
= data
->mspc
->total_rows
;
404 case aoHidd_Sync_Description
:
405 *msg
->storage
= (IPTR
)data
->description
;
408 case aoHidd_Sync_HMin
:
409 *msg
->storage
= data
->hmin
;
412 case aoHidd_Sync_HMax
:
413 *msg
->storage
= data
->hmax
;
416 case aoHidd_Sync_VMin
:
417 *msg
->storage
= data
->vmin
;
420 case aoHidd_Sync_VMax
:
421 *msg
->storage
= data
->vmax
;
424 case aoHidd_Sync_Flags
:
425 *msg
->storage
= data
->flags
;
428 case aoHidd_Sync_Variable
:
429 *msg
->storage
= (data
->InternalFlags
& SYNC_VARIABLE
) ? TRUE
: FALSE
;
432 case aoHidd_Sync_MonitorSpec
:
433 *msg
->storage
= (IPTR
)data
->mspc
;
436 case aoHidd_Sync_GfxHidd
:
437 *msg
->storage
= (IPTR
)data
->gfxhidd
;
441 D(bug("!!! TRYING TO GET UNKNOWN ATTR FROM SYNC OBJECT !!!\n"));
442 OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
449 OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
452 /****************************************************************************************/
454 void Sync__Root__Set(OOP_Class
*cl
, OOP_Object
*o
, struct pRoot_Set
*msg
)
456 struct sync_data
*data
= OOP_INST_DATA(cl
, o
);
458 /* Set actually works only if the object is variable */
459 if (data
->InternalFlags
& SYNC_VARIABLE
)
461 struct class_static_data
*csd
= CSD(cl
);
462 BOOL notify_driver
= parse_sync_tags(csd
, data
, msg
->attrList
, FALSE
);
465 HIDD_Gfx_SetMode(data
->gfxhidd
, (OOP_Object
*)data
->mspc
->ms_Object
);
468 OOP_DoSuperMethod(cl
, o
, &msg
->mID
);
471 /****************************************************************************************/
474 * A backwards compatibility callback that recalculates new htotal value from
475 * MonitorSpec->total_colorclocks.
476 * Future sync editor for AROS will not have to use this API. Instead it is
477 * suggested to use OOP API on a sync object, obtained via VecInfo. It is
478 * more straightforward and flexible.
481 static LONG
do_monitor(struct MonitorSpec
*mspc
)
483 /* Extract sync object data from the MonitorSpec */
484 OOP_Object
*obj
= (OOP_Object
*)mspc
->ms_Object
;
485 OOP_Class
*cl
= OOP_OCLASS(obj
);
486 struct sync_data
*data
= OOP_INST_DATA(cl
, obj
);
487 struct class_static_data
*csd
= CSD(cl
);
489 data
->htotal
= 100000000 / mspc
->total_colorclocks
/ 28 / data
->pixelclock
;
490 HIDD_Gfx_SetMode(data
->gfxhidd
, obj
);
495 /****************************************************************************************/
498 * Parses the tags supplied in 'tags' and puts the result into 'data'.
499 * It also checks to see if all needed attrs are supplied.
500 * Return value is treated as:
501 * - Creation time (init = TRUE) - success/failure flag.
502 * - Set method (init = FALSE) - TRUE if something changed and display driver needs to be notified.
504 static BOOL
parse_sync_tags(struct class_static_data
*csd
, struct sync_data
*data
, struct TagItem
*tags
, BOOL init
)
506 struct Library
*UtilityBase
= csd
->cs_UtilityBase
;
507 UWORD hsync_start
= 0;
508 UWORD vsync_start
= 0;
509 UWORD hsync_end
= data
->hdisp
;
510 UWORD vsync_end
= data
->vdisp
;
511 BOOL have_hsync_start
= FALSE
;
512 BOOL have_hsync_end
= FALSE
;
513 BOOL have_vsync_start
= FALSE
;
514 BOOL have_vsync_end
= FALSE
;
515 BOOL have_htotal
= FALSE
;
516 BOOL have_vtotal
= FALSE
;
517 BOOL change_totclk
= init
;
518 BOOL notify_driver
= FALSE
;
519 struct TagItem
*tag
, *tstate
= tags
;
522 * Parse sync signal parameters. They may come either as start, stop and total
523 * values (which most of drivers use), or as LinuxFB-style specification (margins and
525 * The latter specification is deprecated since no drivers except LinuxFB (which is
526 * broken anyway) use it.
528 while ((tag
= NextTagItem(&tstate
)))
532 if (IS_SYNC_ATTR(tag
->ti_Tag
, idx
))
536 case aoHidd_Sync_PixelClock
:
537 data
->pixelclock
= tag
->ti_Data
;
538 change_totclk
= TRUE
;
541 case aoHidd_Sync_PixelTime
:
543 * According to the HOWTO, PixelTime is one million divided by pixelclock in mHz.
544 * Pixelclock is not always a multiple of 1 mHz, but it seems to always be a multiple
545 * of 1 kHz. We rely on this fact in order to be able to calculate everything in integers.
546 * Anyway, this attribute is deprecated, don't use it.
547 * I intentionally don't simplify this expression in order to make it clear. Let's leave
548 * it to the compiler - sonic.
550 data
->pixelclock
= (1000000000 / tag
->ti_Data
) * 1000;
551 change_totclk
= TRUE
;
554 case aoHidd_Sync_HSyncStart
:
555 hsync_start
= tag
->ti_Data
;
556 have_hsync_start
= TRUE
;
559 case aoHidd_Sync_HSyncEnd
:
560 hsync_end
= tag
->ti_Data
;
561 have_hsync_end
= TRUE
;
564 case aoHidd_Sync_VSyncStart
:
565 vsync_start
= tag
->ti_Data
;
566 have_vsync_start
= TRUE
;
569 case aoHidd_Sync_VSyncEnd
:
570 vsync_end
= tag
->ti_Data
;
571 have_vsync_end
= TRUE
;
574 case aoHidd_Sync_HTotal
:
575 data
->htotal
= tag
->ti_Data
;
577 change_totclk
= TRUE
;
580 case aoHidd_Sync_VTotal
:
581 data
->mspc
->total_rows
= tag
->ti_Data
;
583 notify_driver
= TRUE
;
589 D(bug("[sync] PixelClock is set to %u\n", data
->pixelclock
));
592 * Old LFB-style specification needs to be processed in a particular order,
593 * so we do it here, if needed.
595 if (!have_hsync_start
)
597 tag
= FindTagItem(aHidd_Sync_RightMargin
, tags
);
600 hsync_start
= data
->hdisp
+ tag
->ti_Data
;
601 have_hsync_start
= TRUE
;
607 tag
= FindTagItem(aHidd_Sync_HSyncLength
, tags
);
610 hsync_end
= hsync_start
+ tag
->ti_Data
;
611 have_hsync_end
= TRUE
;
615 if (!have_vsync_start
)
617 tag
= FindTagItem(aHidd_Sync_LowerMargin
, tags
);
620 vsync_start
= data
->vdisp
+ tag
->ti_Data
;
621 have_vsync_start
= TRUE
;
627 tag
= FindTagItem(aHidd_Sync_VSyncLength
, tags
);
630 vsync_end
= vsync_start
+ tag
->ti_Data
;
631 have_vsync_end
= TRUE
;
635 if (have_hsync_start
|| have_hsync_end
|| have_vsync_start
|| have_vsync_end
)
637 /* Sync data changed */
640 /* During object creation this means we need to attach SpecialMonitor to our MonitorSpec. */
641 if (!data
->mspc
->ms_Special
)
643 data
->mspc
->ms_Special
= (struct SpecialMonitor
*)GfxNew(SPECIAL_MONITOR_TYPE
);
644 if (!data
->mspc
->ms_Special
)
647 if (data
->InternalFlags
& SYNC_VARIABLE
)
648 data
->mspc
->ms_Special
->do_monitor
= do_monitor
;
650 data
->mspc
->ms_Flags
|= MSF_REQUEST_SPECIAL
;
651 data
->InternalFlags
|= SYNC_FREE_SPECIALMONITOR
;
654 /* Notification is needed */
655 notify_driver
= TRUE
;
658 if (data
->mspc
->ms_Special
)
660 if (have_hsync_start
)
661 data
->mspc
->ms_Special
->hsync
.asi_Start
= hsync_start
;
663 data
->mspc
->ms_Special
->hsync
.asi_Stop
= hsync_end
;
664 if (have_vsync_start
)
665 data
->mspc
->ms_Special
->vsync
.asi_Start
= vsync_start
;
667 data
->mspc
->ms_Special
->vsync
.asi_Stop
= vsync_end
;
672 UWORD left_margin
= 0;
674 tag
= FindTagItem(aHidd_Sync_LeftMargin
, tags
);
677 left_margin
= tag
->ti_Data
;
678 change_totclk
= TRUE
;
681 * If we have neither HTotal nor LeftMargin, htotal will be equal to hsync_end here.
682 * Previously, hsync_end gets equal to hdisp if no horizontal sync data was specified.
683 * This is done for poor man's drivers which can't provide complete sync information
684 * (like hosted drivers, especially SDL). In this case total = disp, it's better than
685 * nothing. The same is done below with vtotal.
687 data
->htotal
= hsync_end
+ left_margin
;
692 UWORD upper_margin
= 0;
694 tag
= FindTagItem(aHidd_Sync_UpperMargin
, tags
);
697 upper_margin
= tag
->ti_Data
;
698 notify_driver
= TRUE
;
700 data
->mspc
->total_rows
= vsync_end
+ upper_margin
;
705 if (data
->pixelclock
)
706 data
->mspc
->total_colorclocks
= 100000000 / (data
->pixelclock
/ data
->htotal
* 28);
710 * Another kludge for drivers without sync data. Amiga software never expects
711 * to get zero in total_colorclocks, so we have to fill it in with something.
712 * This value will have totally nothing to do with real display refresh rate,
713 * but we can do nothing with it
715 data
->mspc
->total_colorclocks
= VGA_COLORCLOCKS
;
717 /* change_totclk always implies notify_driver, for code simplicity */
718 notify_driver
= TRUE
;
721 return notify_driver
;