2 Copyright © 1995-2013, The AROS Development Team. All rights reserved.
9 /****************************************************************************************/
11 #include <proto/alib.h>
12 #include <proto/graphics.h>
13 #include <proto/oop.h>
14 #include <proto/utility.h>
15 #include <exec/memory.h>
16 #include <graphics/gfxnodes.h>
18 #include <utility/tagitem.h>
19 #include <hidd/graphics.h>
22 #include <aros/debug.h>
26 #include "graphics_intern.h"
29 #define GfxBase csd->cs_GfxBase
31 /****************************************************************************************/
33 static BOOL
parse_sync_tags(struct class_static_data
*csd
, struct sync_data
*data
, struct TagItem
*tags
, BOOL init
);
35 /****************************************************************************************/
37 OOP_Object
*Sync__Root__New(OOP_Class
*cl
, OOP_Object
*o
, struct pRoot_New
*msg
)
39 struct class_static_data
*csd
= CSD(cl
);
40 struct Library
*UtilityBase
= csd
->cs_UtilityBase
;
41 struct Library
*OOPBase
= csd
->cs_OOPBase
;
44 EnterFunc(bug("Sync::New()\n"));
47 * We need graphics.library in order to be able to create MonitorSpec.
48 * we do it here because graphics.hidd is initialized before
51 ObtainSemaphore(&csd
->sema
);
55 GfxBase
= (void *)OpenLibrary("graphics.library", 41);
59 ReleaseSemaphore(&csd
->sema
);
64 /* Get object from superclass */
65 o
= (OOP_Object
*)OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
68 struct sync_data
*data
= OOP_INST_DATA(cl
, o
);
69 struct TagItem
*tstate
= msg
->attrList
;
74 /* Parse mandatory attributes */
75 while ((tag
= NextTagItem(&tstate
)))
79 if (IS_SYNC_ATTR(tag
->ti_Tag
, idx
))
83 case aoHidd_Sync_HDisp
:
84 data
->hdisp
= tag
->ti_Data
;
87 case aoHidd_Sync_VDisp
:
88 data
->vdisp
= tag
->ti_Data
;
91 case aoHidd_Sync_Flags
:
92 data
->flags
= tag
->ti_Data
;
95 case aoHidd_Sync_Description
:
96 s
= (char *)tag
->ti_Data
;
99 case aoHidd_Sync_BoardNumber
:
100 board
= tag
->ti_Data
;
103 case aoHidd_Sync_Variable
:
105 data
->InternalFlags
|= SYNC_VARIABLE
;
108 case aoHidd_Sync_MonitorSpec
:
109 data
->mspc
= (struct MonitorSpec
*)tag
->ti_Data
;
112 case aoHidd_Sync_GfxHidd
:
113 data
->gfxhidd
= (OOP_Object
*)tag
->ti_Data
;
119 /* We must have HDisp, VDisp and GfxHidd */
120 if ((!data
->hdisp
) || (!data
->vdisp
) || (!data
->gfxhidd
))
123 if (ok
&& (!data
->mspc
))
126 * We must have a MonitorSpec. Either it's pre-cooked by the driver
127 * (useful for Amiga(tm) chipset), or we create it ourselves
129 data
->mspc
= (struct MonitorSpec
*)GfxNew(MONITOR_SPEC_TYPE
);
132 data
->mspc
->ms_Node
.xln_Name
= data
->description
;
133 data
->mspc
->ratioh
= RATIO_UNITY
;
134 data
->mspc
->ratiov
= RATIO_UNITY
;
135 InitSemaphore(&data
->mspc
->DisplayInfoDataBaseSemaphore
);
137 data
->InternalFlags
|= SYNC_FREE_MONITORSPEC
;
145 /* By default minimum/maximum bitmap size is equal to display size */
146 data
->hmin
= data
->hdisp
;
147 data
->vmin
= data
->vdisp
;
149 if (GFX__Hidd_Gfx__GetFBModeQuick(csd
->gfxhiddclass
, data
->gfxhidd
) == vHidd_FrameBuffer_Mirrored
)
151 /* But for mirrored framebuffer mode we can have larger bitmaps */
157 data
->hmax
= data
->hdisp
;
158 data
->vmax
= data
->vdisp
;
161 /* Now try to override defaults */
162 tstate
= msg
->attrList
;
163 while ((tag
= NextTagItem(&tstate
)))
167 Hidd_Sync_Switch(tag
->ti_Tag
, idx
)
169 case aoHidd_Sync_HMin
:
170 data
->hmin
= tag
->ti_Data
;
173 case aoHidd_Sync_HMax
:
174 data
->hmax
= tag
->ti_Data
;
177 case aoHidd_Sync_VMin
:
178 data
->vmin
= tag
->ti_Data
;
181 case aoHidd_Sync_VMax
:
182 data
->vmax
= tag
->ti_Data
;
187 /* Format description */
190 char *d
= data
->description
;
191 int dlen
= sizeof(data
->description
);
200 /* It's a format prefix, let's deal with it */
205 l
= snprintf(d
, dlen
, "%u", (unsigned)board
);
209 l
= snprintf(d
, dlen
, "%u", (unsigned)data
->hdisp
);
213 l
= snprintf(d
, dlen
, "%u", (unsigned)data
->vdisp
);
217 /* Just copy over two chars */
220 /* Copy next character only if we have room for it */
231 /* Copy one character */
236 /* If NULL byte has been just transferred, exit, the string is already terminated */
240 /* Increment pointer, decrement length */
244 /* If we have only one byte in the destination left, terminate the string and exit */
253 ok
= parse_sync_tags(CSD(cl
), data
, msg
->attrList
, TRUE
);
258 /* Set object pointer and add the MonitorSpec to the GfxBase->MonitorList */
259 data
->mspc
->ms_Object
= (void *)o
;
261 ObtainSemaphore(GfxBase
->MonitorListSemaphore
);
262 Enqueue(&GfxBase
->MonitorList
, (struct Node
*)data
->mspc
);
263 ReleaseSemaphore(GfxBase
->MonitorListSemaphore
);
269 OOP_MethodID dispose_mid
;
271 D(bug("!!! ERROR PARSING SYNC ATTRS IN Sync::New() !!!\n"));
273 dispose_mid
= OOP_GetMethodID(IID_Root
, moRoot_Dispose
);
274 OOP_CoerceMethod(cl
, o
, &dispose_mid
);
281 /****************************************************************************************/
283 VOID
Sync__Root__Dispose(OOP_Class
*cl
, OOP_Object
*o
, OOP_Msg msg
)
285 struct sync_data
*data
= OOP_INST_DATA(cl
, o
);
286 struct class_static_data
*csd
= CSD(cl
);
288 /* First we remove the MonitorSpec from the list, if it's our MonitorSpec
289 and it's in the list */
290 if ((data
->InternalFlags
& SYNC_FREE_MONITORSPEC
) &&
291 data
->mspc
->ms_Node
.xln_Succ
) {
293 ObtainSemaphore(GfxBase
->MonitorListSemaphore
);
294 Remove((struct Node
*)data
->mspc
);
295 ReleaseSemaphore(GfxBase
->MonitorListSemaphore
);
298 /* Then we dispose things that we created */
299 if (data
->InternalFlags
& SYNC_FREE_SPECIALMONITOR
)
300 GfxFree(&data
->mspc
->ms_Special
->spm_Node
);
301 if (data
->InternalFlags
& SYNC_FREE_MONITORSPEC
)
302 GfxFree(&data
->mspc
->ms_Node
);
304 OOP_DoSuperMethod(cl
, o
, msg
);
307 /****************************************************************************************/
309 VOID
Sync__Root__Get(OOP_Class
*cl
, OOP_Object
*o
, struct pRoot_Get
*msg
)
311 struct sync_data
*data
;
312 struct class_static_data
*csd
;
315 data
= OOP_INST_DATA(cl
, o
);
318 if (IS_SYNC_ATTR(msg
->attrID
, idx
))
320 UWORD hsync_start
, hsync_end
, vsync_start
, vsync_end
;
322 if (data
->mspc
->ms_Special
) {
323 hsync_start
= data
->mspc
->ms_Special
->hsync
.asi_Start
;
324 hsync_end
= data
->mspc
->ms_Special
->hsync
.asi_Stop
;
325 vsync_start
= data
->mspc
->ms_Special
->vsync
.asi_Start
;
326 vsync_end
= data
->mspc
->ms_Special
->vsync
.asi_Stop
;
328 /* Special failback values that will result in zero margins and sync lengths */
329 hsync_start
= data
->hdisp
;
330 hsync_end
= data
->hdisp
;
331 vsync_start
= data
->vdisp
;
332 vsync_end
= data
->vdisp
;
337 case aoHidd_Sync_PixelTime
:
338 if (data
->pixelclock
) {
339 ULONG khz
= data
->pixelclock
/ 1000;
341 *msg
->storage
= 1000000000 / khz
;
345 case aoHidd_Sync_PixelClock
:
346 *msg
->storage
= data
->pixelclock
;
349 case aoHidd_Sync_LeftMargin
:
350 *msg
->storage
= data
->htotal
- hsync_end
;
353 case aoHidd_Sync_RightMargin
:
354 *msg
->storage
= hsync_start
- data
->hdisp
;
357 case aoHidd_Sync_HSyncLength
:
358 *msg
->storage
= hsync_end
- hsync_start
;
361 case aoHidd_Sync_UpperMargin
:
362 *msg
->storage
= data
->mspc
->total_rows
- vsync_end
;
365 case aoHidd_Sync_LowerMargin
:
366 *msg
->storage
= vsync_end
- data
->vdisp
;
369 case aoHidd_Sync_VSyncLength
:
370 *msg
->storage
= vsync_end
- vsync_start
;
373 case aoHidd_Sync_HDisp
:
374 *msg
->storage
= data
->hdisp
;
377 case aoHidd_Sync_VDisp
:
378 *msg
->storage
= data
->vdisp
;
381 case aoHidd_Sync_HSyncStart
:
382 *msg
->storage
= hsync_start
;
385 case aoHidd_Sync_HSyncEnd
:
386 *msg
->storage
= hsync_end
;
389 case aoHidd_Sync_HTotal
:
390 *msg
->storage
= data
->htotal
;
393 case aoHidd_Sync_VSyncStart
:
394 *msg
->storage
= vsync_start
;
397 case aoHidd_Sync_VSyncEnd
:
398 *msg
->storage
= vsync_end
;
401 case aoHidd_Sync_VTotal
:
402 *msg
->storage
= data
->mspc
->total_rows
;
405 case aoHidd_Sync_Description
:
406 *msg
->storage
= (IPTR
)data
->description
;
409 case aoHidd_Sync_HMin
:
410 *msg
->storage
= data
->hmin
;
413 case aoHidd_Sync_HMax
:
414 *msg
->storage
= data
->hmax
;
417 case aoHidd_Sync_VMin
:
418 *msg
->storage
= data
->vmin
;
421 case aoHidd_Sync_VMax
:
422 *msg
->storage
= data
->vmax
;
425 case aoHidd_Sync_Flags
:
426 *msg
->storage
= data
->flags
;
429 case aoHidd_Sync_Variable
:
430 *msg
->storage
= (data
->InternalFlags
& SYNC_VARIABLE
) ? TRUE
: FALSE
;
433 case aoHidd_Sync_MonitorSpec
:
434 *msg
->storage
= (IPTR
)data
->mspc
;
437 case aoHidd_Sync_GfxHidd
:
438 *msg
->storage
= (IPTR
)data
->gfxhidd
;
442 D(bug("!!! TRYING TO GET UNKNOWN ATTR FROM SYNC OBJECT !!!\n"));
443 OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
450 OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
453 /****************************************************************************************/
455 void Sync__Root__Set(OOP_Class
*cl
, OOP_Object
*o
, struct pRoot_Set
*msg
)
457 struct sync_data
*data
= OOP_INST_DATA(cl
, o
);
459 /* Set actually works only if the object is variable */
460 if (data
->InternalFlags
& SYNC_VARIABLE
)
462 struct class_static_data
*csd
= CSD(cl
);
463 BOOL notify_driver
= parse_sync_tags(csd
, data
, msg
->attrList
, FALSE
);
466 HIDD_Gfx_SetMode(data
->gfxhidd
, (OOP_Object
*)data
->mspc
->ms_Object
);
469 OOP_DoSuperMethod(cl
, o
, &msg
->mID
);
472 /****************************************************************************************/
475 * A backwards compatibility callback that recalculates new htotal value from
476 * MonitorSpec->total_colorclocks.
477 * Future sync editor for AROS will not have to use this API. Instead it is
478 * suggested to use OOP API on a sync object, obtained via VecInfo. It is
479 * more straightforward and flexible.
482 static LONG
do_monitor(struct MonitorSpec
*mspc
)
484 /* Extract sync object data from the MonitorSpec */
485 OOP_Object
*obj
= (OOP_Object
*)mspc
->ms_Object
;
486 OOP_Class
*cl
= OOP_OCLASS(obj
);
487 struct sync_data
*data
= OOP_INST_DATA(cl
, obj
);
488 struct class_static_data
*csd
= CSD(cl
);
490 data
->htotal
= 100000000 / mspc
->total_colorclocks
/ 28 / data
->pixelclock
;
491 HIDD_Gfx_SetMode(data
->gfxhidd
, obj
);
496 /****************************************************************************************/
499 * Parses the tags supplied in 'tags' and puts the result into 'data'.
500 * It also checks to see if all needed attrs are supplied.
501 * Return value is treated as:
502 * - Creation time (init = TRUE) - success/failure flag.
503 * - Set method (init = FALSE) - TRUE if something changed and display driver needs to be notified.
505 static BOOL
parse_sync_tags(struct class_static_data
*csd
, struct sync_data
*data
, struct TagItem
*tags
, BOOL init
)
507 struct Library
*UtilityBase
= csd
->cs_UtilityBase
;
508 UWORD hsync_start
= 0;
509 UWORD vsync_start
= 0;
510 UWORD hsync_end
= data
->hdisp
;
511 UWORD vsync_end
= data
->vdisp
;
512 BOOL have_hsync_start
= FALSE
;
513 BOOL have_hsync_end
= FALSE
;
514 BOOL have_vsync_start
= FALSE
;
515 BOOL have_vsync_end
= FALSE
;
516 BOOL have_htotal
= FALSE
;
517 BOOL have_vtotal
= FALSE
;
518 BOOL change_totclk
= init
;
519 BOOL notify_driver
= FALSE
;
520 struct TagItem
*tag
, *tstate
= tags
;
523 * Parse sync signal parameters. They may come either as start, stop and total
524 * values (which most of drivers use), or as LinuxFB-style specification (margins and
526 * The latter specification is deprecated since no drivers except LinuxFB (which is
527 * broken anyway) use it.
529 while ((tag
= NextTagItem(&tstate
)))
533 if (IS_SYNC_ATTR(tag
->ti_Tag
, idx
))
537 case aoHidd_Sync_PixelClock
:
538 data
->pixelclock
= tag
->ti_Data
;
539 change_totclk
= TRUE
;
542 case aoHidd_Sync_PixelTime
:
544 * According to the HOWTO, PixelTime is one million divided by pixelclock in mHz.
545 * Pixelclock is not always a multiple of 1 mHz, but it seems to always be a multiple
546 * of 1 kHz. We rely on this fact in order to be able to calculate everything in integers.
547 * Anyway, this attribute is deprecated, don't use it.
548 * I intentionally don't simplify this expression in order to make it clear. Let's leave
549 * it to the compiler - sonic.
551 data
->pixelclock
= (1000000000 / tag
->ti_Data
) * 1000;
552 change_totclk
= TRUE
;
555 case aoHidd_Sync_HSyncStart
:
556 hsync_start
= tag
->ti_Data
;
557 have_hsync_start
= TRUE
;
560 case aoHidd_Sync_HSyncEnd
:
561 hsync_end
= tag
->ti_Data
;
562 have_hsync_end
= TRUE
;
565 case aoHidd_Sync_VSyncStart
:
566 vsync_start
= tag
->ti_Data
;
567 have_vsync_start
= TRUE
;
570 case aoHidd_Sync_VSyncEnd
:
571 vsync_end
= tag
->ti_Data
;
572 have_vsync_end
= TRUE
;
575 case aoHidd_Sync_HTotal
:
576 data
->htotal
= tag
->ti_Data
;
578 change_totclk
= TRUE
;
581 case aoHidd_Sync_VTotal
:
582 data
->mspc
->total_rows
= tag
->ti_Data
;
584 notify_driver
= TRUE
;
590 D(bug("[sync] PixelClock is set to %u\n", data
->pixelclock
));
593 * Old LFB-style specification needs to be processed in a particular order,
594 * so we do it here, if needed.
596 if (!have_hsync_start
)
598 tag
= FindTagItem(aHidd_Sync_RightMargin
, tags
);
601 hsync_start
= data
->hdisp
+ tag
->ti_Data
;
602 have_hsync_start
= TRUE
;
608 tag
= FindTagItem(aHidd_Sync_HSyncLength
, tags
);
611 hsync_end
= hsync_start
+ tag
->ti_Data
;
612 have_hsync_end
= TRUE
;
616 if (!have_vsync_start
)
618 tag
= FindTagItem(aHidd_Sync_LowerMargin
, tags
);
621 vsync_start
= data
->vdisp
+ tag
->ti_Data
;
622 have_vsync_start
= TRUE
;
628 tag
= FindTagItem(aHidd_Sync_VSyncLength
, tags
);
631 vsync_end
= vsync_start
+ tag
->ti_Data
;
632 have_vsync_end
= TRUE
;
636 if (have_hsync_start
|| have_hsync_end
|| have_vsync_start
|| have_vsync_end
)
638 /* Sync data changed */
641 /* During object creation this means we need to attach SpecialMonitor to our MonitorSpec. */
642 if (!data
->mspc
->ms_Special
)
644 data
->mspc
->ms_Special
= (struct SpecialMonitor
*)GfxNew(SPECIAL_MONITOR_TYPE
);
645 if (!data
->mspc
->ms_Special
)
648 if (data
->InternalFlags
& SYNC_VARIABLE
)
649 data
->mspc
->ms_Special
->do_monitor
= do_monitor
;
651 data
->mspc
->ms_Flags
|= MSF_REQUEST_SPECIAL
;
652 data
->InternalFlags
|= SYNC_FREE_SPECIALMONITOR
;
655 /* Notification is needed */
656 notify_driver
= TRUE
;
659 if (data
->mspc
->ms_Special
)
661 if (have_hsync_start
)
662 data
->mspc
->ms_Special
->hsync
.asi_Start
= hsync_start
;
664 data
->mspc
->ms_Special
->hsync
.asi_Stop
= hsync_end
;
665 if (have_vsync_start
)
666 data
->mspc
->ms_Special
->vsync
.asi_Start
= vsync_start
;
668 data
->mspc
->ms_Special
->vsync
.asi_Stop
= vsync_end
;
673 UWORD left_margin
= 0;
675 tag
= FindTagItem(aHidd_Sync_LeftMargin
, tags
);
678 left_margin
= tag
->ti_Data
;
679 change_totclk
= TRUE
;
682 * If we have neither HTotal nor LeftMargin, htotal will be equal to hsync_end here.
683 * Previously, hsync_end gets equal to hdisp if no horizontal sync data was specified.
684 * This is done for poor man's drivers which can't provide complete sync information
685 * (like hosted drivers, especially SDL). In this case total = disp, it's better than
686 * nothing. The same is done below with vtotal.
688 data
->htotal
= hsync_end
+ left_margin
;
693 UWORD upper_margin
= 0;
695 tag
= FindTagItem(aHidd_Sync_UpperMargin
, tags
);
698 upper_margin
= tag
->ti_Data
;
699 notify_driver
= TRUE
;
701 data
->mspc
->total_rows
= vsync_end
+ upper_margin
;
706 if (data
->pixelclock
)
707 data
->mspc
->total_colorclocks
= 100000000 / (data
->pixelclock
/ data
->htotal
* 28);
711 * Another kludge for drivers without sync data. Amiga software never expects
712 * to get zero in total_colorclocks, so we have to fill it in with something.
713 * This value will have totally nothing to do with real display refresh rate,
714 * but we can do nothing with it
716 data
->mspc
->total_colorclocks
= VGA_COLORCLOCKS
;
718 /* change_totclk always implies notify_driver, for code simplicity */
719 notify_driver
= TRUE
;
722 return notify_driver
;