mirror of
https://git.code.sf.net/p/quake/quake2forge
synced 2024-12-12 13:42:21 +00:00
555 lines
12 KiB
C
555 lines
12 KiB
C
|
#include <stdio.h>
|
||
|
#include <X11/X.h>
|
||
|
#include <X11/Xlib.h>
|
||
|
#include <X11/Xutil.h>
|
||
|
#include <stropts.h>
|
||
|
#include <poll.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/time.h>
|
||
|
|
||
|
#include "nvsession.h"
|
||
|
|
||
|
#define NDGA_IMPLEMENTATION 1
|
||
|
#include "NRegion.h"
|
||
|
#include "ndga.h"
|
||
|
|
||
|
/*
|
||
|
* newt equivalent of DGA
|
||
|
*
|
||
|
* Written by: Jim Hanko
|
||
|
*/
|
||
|
|
||
|
#define NS_PER_SEC (1000LL * 1000LL * 1000LL)
|
||
|
|
||
|
/* #define DEBUG */
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
#define DBG(x) x
|
||
|
#else
|
||
|
#define DBG(x)
|
||
|
#endif
|
||
|
|
||
|
int
|
||
|
ndga_get_eventmask(void)
|
||
|
{
|
||
|
return (VisibilityChangeMask | ExposureMask | StructureNotifyMask);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ndga_destroy(struct ndga_state * sp)
|
||
|
{
|
||
|
if (sp) {
|
||
|
if (sp->session && sp->sent_clip) {
|
||
|
sp->sent_clip = 0;
|
||
|
nvSessionClipRegion(sp->session, NULL);
|
||
|
}
|
||
|
NDestroyRegion(sp->region);
|
||
|
NDestroyRegion(sp->clip);
|
||
|
NDestroyRegion(sp->lastclip);
|
||
|
NDestroyRegion(sp->screenArea);
|
||
|
free(sp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct ndga_state *
|
||
|
ndga_new(Display *display, Window window, nvSession session,
|
||
|
int xalign, int yalign)
|
||
|
{
|
||
|
struct ndga_state *sp;
|
||
|
Window *children;
|
||
|
int nchildren;
|
||
|
XWindowAttributes attr;
|
||
|
int retval;
|
||
|
|
||
|
sp = (struct ndga_state *) malloc(sizeof(*sp));
|
||
|
if (sp == NULL)
|
||
|
return (NULL);
|
||
|
memset(sp, 0, sizeof(*sp));
|
||
|
|
||
|
sp->display = display;
|
||
|
sp->window = window;
|
||
|
|
||
|
sp->xalign = xalign;
|
||
|
sp->yalign = yalign;
|
||
|
|
||
|
sp->is_visible = 1;
|
||
|
sp->is_unobscured = 1;
|
||
|
sp->is_open = 1;
|
||
|
sp->was_open = 1;
|
||
|
sp->was_unobscured = 0;
|
||
|
sp->is_changed = 1;
|
||
|
sp->new_clip = 1;
|
||
|
sp->busy = 0;
|
||
|
|
||
|
sp->clipEvent = 1;
|
||
|
|
||
|
sp->session = session;
|
||
|
|
||
|
XQueryTree(sp->display, sp->window, &sp->root, &sp->parent,
|
||
|
&children, &nchildren);
|
||
|
|
||
|
retval = XGetWindowAttributes(sp->display, sp->root, &attr);
|
||
|
if (retval == 0) {
|
||
|
fprintf(stderr, "Can't get window attributes\n");
|
||
|
free(sp);
|
||
|
return (NULL);
|
||
|
}
|
||
|
sp->region = NCreateRegion();
|
||
|
sp->clip = NCreateRegion();
|
||
|
sp->lastclip = NCreateRegion();
|
||
|
|
||
|
sp->screenArea = NCreateRegion();
|
||
|
|
||
|
sp->screenRect.x = attr.x;
|
||
|
sp->screenRect.y = attr.y;
|
||
|
sp->screenRect.width = attr.width;
|
||
|
sp->screenRect.height = attr.height;
|
||
|
DBG(fprintf(stderr, "screen %d,%d %dx%d\n",
|
||
|
sp->screenRect.x, sp->screenRect.y,
|
||
|
sp->screenRect.width, sp->screenRect.height));
|
||
|
NUnionRectWithRegion(&sp->screenRect, sp->screenArea, sp->screenArea);
|
||
|
|
||
|
return (sp);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
get_win_info(struct ndga_state * sp)
|
||
|
{
|
||
|
int depth;
|
||
|
int xr, yr, wr, hr, border;
|
||
|
Window w, root, parent, *children;
|
||
|
int nchildren;
|
||
|
int x, y;
|
||
|
XWindowAttributes attr;
|
||
|
int retval;
|
||
|
|
||
|
|
||
|
DBG(fprintf(stderr, "get_win_info()\n"));
|
||
|
retval = XGetWindowAttributes(sp->display, sp->window, &attr);
|
||
|
if (retval == 0) {
|
||
|
fprintf(stderr, "Can't get window attributes\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
sp->is_open = (attr.map_state == IsViewable);
|
||
|
DBG(fprintf(stderr, "ismapped %d\n", sp->is_open));
|
||
|
|
||
|
x = attr.x + attr.border_width;
|
||
|
y = attr.y + attr.border_width;
|
||
|
sp->w = attr.width;
|
||
|
sp->h = attr.height;
|
||
|
if (sp->xalign > 1)
|
||
|
sp->w -= (sp->w % sp->xalign);
|
||
|
if (sp->yalign > 1)
|
||
|
sp->h -= (sp->h % sp->yalign);
|
||
|
|
||
|
w = sp->parent;
|
||
|
while (w && w != sp->root) {
|
||
|
XGetGeometry(sp->display, w, &root, &xr, &yr, &wr, &hr,
|
||
|
&border, &depth);
|
||
|
x += xr + border;
|
||
|
y += yr + border;
|
||
|
XQueryTree(sp->display, w, &root, &w, &children, &nchildren);
|
||
|
}
|
||
|
|
||
|
sp->x = x;
|
||
|
sp->y = y;
|
||
|
|
||
|
sp->visRect.x = sp->x;
|
||
|
sp->visRect.y = sp->y;
|
||
|
sp->visRect.width = sp->w;
|
||
|
sp->visRect.height = sp->h;
|
||
|
|
||
|
DBG(fprintf(stderr, "X %d Y %d W %d H %d\n", sp->x, sp->y,
|
||
|
sp->w, sp->h));
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
rdump(void *a, int x, int y, int w, int h)
|
||
|
{
|
||
|
struct ndga_state *s = (struct ndga_state *) a;
|
||
|
fprintf(stderr, "%d,%d %dx%d\n", x, y, w, h);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
ndga_handle_change(struct ndga_state *sp)
|
||
|
{
|
||
|
NRectangle rectangle;
|
||
|
|
||
|
get_win_info(sp);
|
||
|
if (sp->is_open && sp->is_unobscured && !sp->was_unobscured) {
|
||
|
rectangle.x = 0;
|
||
|
rectangle.y = 0;
|
||
|
rectangle.width = sp->w;
|
||
|
rectangle.height = sp->h;
|
||
|
NDestroyRegion(sp->region);
|
||
|
sp->region = NCreateRegion();
|
||
|
NUnionRectWithRegion(&rectangle, sp->region,
|
||
|
sp->region);
|
||
|
|
||
|
DBG(fprintf(stderr, "Full clip\n"));
|
||
|
} else if (!sp->is_open || !sp->is_visible) {
|
||
|
NDestroyRegion(sp->region);
|
||
|
sp->region = NCreateRegion();
|
||
|
}
|
||
|
|
||
|
NUnionRegion(sp->region, sp->region, sp->clip);
|
||
|
NOffsetRegion(sp->clip, sp->x, sp->y);
|
||
|
NIntersectRegion(sp->clip, sp->screenArea, sp->clip);
|
||
|
if (sp->xalign > 1 || sp->yalign > 1) {
|
||
|
NAlignRegion(sp->clip, sp->clip, sp->x, sp->xalign, sp->y,
|
||
|
sp->yalign);
|
||
|
}
|
||
|
|
||
|
if (sp->x < sp->screenRect.x ||
|
||
|
sp->y < sp->screenRect.y ||
|
||
|
sp->x + sp->w > sp->screenRect.x + sp->screenRect.width ||
|
||
|
sp->y + sp->h > sp->screenRect.y + sp->screenRect.height) {
|
||
|
(void) NClipBox(sp->clip, &sp->visRect);
|
||
|
} else {
|
||
|
sp->visRect.x = sp->x;
|
||
|
sp->visRect.y = sp->y;
|
||
|
sp->visRect.width = sp->w;
|
||
|
sp->visRect.height = sp->h;
|
||
|
}
|
||
|
|
||
|
sp->new_clip = 1;
|
||
|
sp->clipEvent++;
|
||
|
sp->was_unobscured = sp->is_unobscured;
|
||
|
|
||
|
DBG(fprintf(stderr, "\n%s: %d - Clip \n", __FILE__, __LINE__));
|
||
|
DBG(NWalkRegion(sp->clip, rdump, sp));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Keep the session manager informed
|
||
|
*/
|
||
|
static void
|
||
|
update_clip(struct ndga_state *sp)
|
||
|
{
|
||
|
if (!NEqualRegion(sp->clip, sp->lastclip)) {
|
||
|
NUnionRegion(sp->clip, sp->clip, sp->lastclip);
|
||
|
if (sp->session && sp->sent_clip) {
|
||
|
nvSessionClipRegion(sp->session, sp->clip);
|
||
|
}
|
||
|
}
|
||
|
sp->new_clip = 0;
|
||
|
}
|
||
|
|
||
|
#ifdef X_VISIBILITY_BUG
|
||
|
|
||
|
int fix_visibility = -1;
|
||
|
|
||
|
/*
|
||
|
* The current X server will not report visibility events if the
|
||
|
* window was partially obscured and it become more obscured. So,
|
||
|
* we periodically poll to get an accurate clip list.
|
||
|
*
|
||
|
* Expected to be fixed by Solaris 2.8
|
||
|
*/
|
||
|
static void
|
||
|
ndga_workaround(struct ndga_state * sp)
|
||
|
{
|
||
|
char *s;
|
||
|
|
||
|
if (fix_visibility == -1) {
|
||
|
if ((s = getenv("CORONA_VISIBILITY_FIX")) != NULL &&
|
||
|
(*s == 'T' || *s == 't' || *s == '1')) {
|
||
|
fix_visibility = 1;
|
||
|
} else {
|
||
|
fix_visibility = 0;
|
||
|
}
|
||
|
}
|
||
|
if (fix_visibility != 1)
|
||
|
return;
|
||
|
|
||
|
if (sp->is_visible && !sp->is_unobscured && sp->sent_clip) {
|
||
|
hrtime_t now;
|
||
|
static hrtime_t last;
|
||
|
|
||
|
now = gethrtime();
|
||
|
if (now - last > NS_PER_SEC) {
|
||
|
Window w;
|
||
|
|
||
|
DBG(fprintf(stderr, "Poll for clip\n"));
|
||
|
w = XCreateSimpleWindow(sp->display, sp->window,
|
||
|
0, 0, 30000, 30000, 0, 0, 0);
|
||
|
XMapWindow(sp->display, w);
|
||
|
XFlush(sp->display);
|
||
|
XDestroyWindow(sp->display, w);
|
||
|
XFlush(sp->display);
|
||
|
/*
|
||
|
* No need for sync here
|
||
|
*/
|
||
|
sp->clearOnExpose = 1;
|
||
|
|
||
|
last = gethrtime();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif /* X_VISIBILITY_BUG */
|
||
|
|
||
|
/*
|
||
|
* attempt to start direct io; returns 1 if visible and sets the
|
||
|
* window's location in xp, yp, wp, hp. is_obscured is set if all
|
||
|
* parts of the window are visible. clipEvent contains an event
|
||
|
* counter for changes in the clip list. When a caller observes
|
||
|
* a change in the counter, it should obtain the new clip.
|
||
|
*
|
||
|
* Returns 0 if the window is not visible.
|
||
|
*/
|
||
|
int
|
||
|
ndga_start(struct ndga_state * sp,
|
||
|
int *xp, /* x */
|
||
|
int *yp, /* y */
|
||
|
int *wp, /* width */
|
||
|
int *hp, /* height */
|
||
|
int *is_unobscured, /* 1 if clipped, 0 if not */
|
||
|
int *clipEvent /* clip event count */)
|
||
|
{
|
||
|
char *loc;
|
||
|
|
||
|
if (!sp)
|
||
|
return (0);
|
||
|
|
||
|
if (sp->is_changed) {
|
||
|
ndga_handle_change(sp);
|
||
|
sp->is_changed = 0;
|
||
|
}
|
||
|
|
||
|
if (sp->new_clip) {
|
||
|
update_clip(sp);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If fully visible or if partially visible and the client is
|
||
|
* expecting to handle clipping, allow it to go through
|
||
|
*/
|
||
|
if (sp->is_open && sp->is_visible &&
|
||
|
(sp->is_unobscured ||
|
||
|
(clipEvent != NULL && is_unobscured != NULL))) {
|
||
|
if (sp->session && sp->sent_clip == 0) {
|
||
|
nvSessionClipRegion(sp->session, sp->clip);
|
||
|
sp->sent_clip = 1;
|
||
|
}
|
||
|
|
||
|
*xp = sp->visRect.x;
|
||
|
*wp = sp->visRect.width;
|
||
|
|
||
|
*yp = sp->visRect.y;
|
||
|
*hp = sp->visRect.height;
|
||
|
|
||
|
sp->busy = 1;
|
||
|
|
||
|
if (clipEvent)
|
||
|
*clipEvent = sp->clipEvent;
|
||
|
|
||
|
if (is_unobscured)
|
||
|
*is_unobscured = sp->is_unobscured;
|
||
|
|
||
|
DBG(fprintf(stderr, "ndga_start() = 1\n"));
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
if (sp->is_open && sp->is_visible) {
|
||
|
/*
|
||
|
* Assume it will just go through X; slow it down
|
||
|
*/
|
||
|
poll(0, 0, sp->w * sp->h / 450);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* not OK
|
||
|
*/
|
||
|
if (sp->session && sp->sent_clip) {
|
||
|
sp->sent_clip = 0;
|
||
|
nvSessionClipRegion(sp->session, NULL);
|
||
|
}
|
||
|
|
||
|
if (clipEvent)
|
||
|
*clipEvent = sp->clipEvent;
|
||
|
|
||
|
if (is_unobscured)
|
||
|
*is_unobscured = 0;
|
||
|
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* update the argument to hold the current clip-list and and return
|
||
|
* it. If the clip argument is NULL, a new region is allocated. It
|
||
|
* is the responsibility of the caller to free the clip.
|
||
|
*/
|
||
|
NRegion
|
||
|
ndga_clip(struct ndga_state * sp, NRegion clip)
|
||
|
{
|
||
|
if (!clip)
|
||
|
clip = NCreateRegion();
|
||
|
|
||
|
/*
|
||
|
* Use union as copy
|
||
|
*/
|
||
|
(void) NUnionRegion(sp->clip, sp->clip, clip);
|
||
|
return(clip);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ndga_done(struct ndga_state * sp)
|
||
|
{
|
||
|
if (!sp)
|
||
|
return;
|
||
|
|
||
|
if (sp->is_changed) {
|
||
|
ndga_handle_change(sp);
|
||
|
sp->is_changed = 0;
|
||
|
}
|
||
|
|
||
|
if (sp->new_clip) {
|
||
|
update_clip(sp);
|
||
|
}
|
||
|
|
||
|
sp->busy = 0;
|
||
|
|
||
|
#ifdef X_VISIBILITY_BUG
|
||
|
ndga_workaround(sp);
|
||
|
#endif /* X_VISIBILITY_BUG */
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ndga_process_event(struct ndga_state * sp, XEvent * event)
|
||
|
{
|
||
|
NRectangle rectangle;
|
||
|
|
||
|
if (!sp)
|
||
|
return;
|
||
|
|
||
|
if (event->xany.type == Expose) {
|
||
|
#ifdef X_VISIBILITY_BUG
|
||
|
if (sp->clearOnExpose) {
|
||
|
NDestroyRegion(sp->region);
|
||
|
sp->region = NCreateRegion();
|
||
|
sp->clearOnExpose = 0;
|
||
|
}
|
||
|
#endif /* X_VISIBILITY_BUG */
|
||
|
rectangle.x = event->xexpose.x;
|
||
|
rectangle.y = event->xexpose.y;
|
||
|
rectangle.width = event->xexpose.width;
|
||
|
rectangle.height = event->xexpose.height;
|
||
|
NUnionRectWithRegion(&rectangle, sp->region, sp->region);
|
||
|
|
||
|
DBG(fprintf(stderr, "Expose: %d,%d %dx%d\n",
|
||
|
event->xexpose.x,
|
||
|
event->xexpose.y,
|
||
|
event->xexpose.width,
|
||
|
event->xexpose.height));
|
||
|
if (event->xexpose.count == 0) {
|
||
|
/*
|
||
|
* Handle the last in a sequence of expose events.
|
||
|
*/
|
||
|
sp->is_changed = 1;
|
||
|
}
|
||
|
} else if (event->xany.type == VisibilityNotify) {
|
||
|
sp->is_visible =
|
||
|
(event->xvisibility.state != VisibilityFullyObscured);
|
||
|
sp->is_unobscured =
|
||
|
(event->xvisibility.state == VisibilityUnobscured);
|
||
|
sp->is_changed = 1;
|
||
|
DBG(fprintf(stderr, "Visiblility: %s\n",
|
||
|
(event->xvisibility.state == VisibilityUnobscured) ?
|
||
|
"Unobscured" :
|
||
|
((event->xvisibility.state == VisibilityFullyObscured) ?
|
||
|
"FullyObscured" : "PartuallyObscured")));
|
||
|
if (event->xvisibility.state == VisibilityPartiallyObscured) {
|
||
|
Window w;
|
||
|
DBG(fprintf(stderr, "Request full clip\n"));
|
||
|
|
||
|
NDestroyRegion(sp->region);
|
||
|
sp->region = NCreateRegion();
|
||
|
|
||
|
w = XCreateSimpleWindow(sp->display, sp->window,
|
||
|
0, 0, 30000, 30000, 0, 0, 0);
|
||
|
XMapWindow(sp->display, w);
|
||
|
XFlush(sp->display);
|
||
|
XDestroyWindow(sp->display, w);
|
||
|
XFlush(sp->display);
|
||
|
/*
|
||
|
* sync so we will get expose events all together
|
||
|
*/
|
||
|
XSync(sp->display, False);
|
||
|
}
|
||
|
} else if (event->xany.type == UnmapNotify) {
|
||
|
DBG(fprintf(stderr, "Unmap\n"));
|
||
|
sp->is_changed = 1;
|
||
|
NDestroyRegion(sp->region);
|
||
|
sp->region = NCreateRegion();
|
||
|
} else if (event->xany.type == ConfigureNotify) {
|
||
|
DBG(fprintf(stderr, "Configure\n"));
|
||
|
sp->is_changed = 1;
|
||
|
} else if (event->xany.type == CirculateNotify) {
|
||
|
DBG(fprintf(stderr, "Circulate\n"));
|
||
|
} else if (event->xany.type == MapNotify) {
|
||
|
DBG(fprintf(stderr, "Map\n"));
|
||
|
} else {
|
||
|
DBG(fprintf(stderr, "Event %d\n", event->xany.type));
|
||
|
}
|
||
|
|
||
|
if (XPending(sp->display) != 0)
|
||
|
return;
|
||
|
DBG(fprintf(stderr, "Last Event\n\n"));
|
||
|
|
||
|
if ((sp->on_open || sp->on_close) && sp->is_changed) {
|
||
|
if (sp->is_open != sp->was_open) {
|
||
|
if (!sp->is_open) {
|
||
|
DBG(fprintf(stderr, "I-CLOSED\n"));
|
||
|
if (sp->on_close)
|
||
|
(*sp->on_close) ();
|
||
|
} else {
|
||
|
DBG(fprintf(stderr, "I-OPEN\n"));
|
||
|
if (sp->on_open)
|
||
|
(*sp->on_open) ();
|
||
|
}
|
||
|
|
||
|
sp->was_open = sp->is_open;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sp->is_changed) {
|
||
|
ndga_handle_change(sp);
|
||
|
if (!sp->busy)
|
||
|
sp->is_changed = 0;
|
||
|
}
|
||
|
|
||
|
if (!sp->busy && sp->new_clip) {
|
||
|
update_clip(sp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
ndga_isvisible(ndga_t sp)
|
||
|
{
|
||
|
if (!sp) {
|
||
|
/*
|
||
|
* Safest to say yes?
|
||
|
*/
|
||
|
return (1);
|
||
|
}
|
||
|
|
||
|
if (!sp->busy && sp->is_changed) {
|
||
|
ndga_handle_change(sp);
|
||
|
sp->is_changed = 0;
|
||
|
}
|
||
|
|
||
|
if (!sp->busy && sp->new_clip) {
|
||
|
update_clip(sp);
|
||
|
}
|
||
|
|
||
|
#ifdef X_VISIBILITY_BUG
|
||
|
ndga_workaround(sp);
|
||
|
#endif /* X_VISIBILITY_BUG */
|
||
|
|
||
|
DBG(fprintf(stderr, "is_visible %d\n", sp->is_open && sp->is_visible));
|
||
|
return (sp->is_open && sp->is_visible);
|
||
|
}
|