mirror of
https://git.code.sf.net/p/quake/quake2forge
synced 2024-12-12 13:42:21 +00:00
321 lines
8.5 KiB
C
321 lines
8.5 KiB
C
|
/*
|
||
|
* Brian Schmidt
|
||
|
*
|
||
|
* File: newt.c
|
||
|
* Date: 03/18/99
|
||
|
* Revision: 1.9
|
||
|
*
|
||
|
* This file contains the routines necessary to send Quake video to
|
||
|
* a NewT.
|
||
|
*/
|
||
|
#pragma ident "@(#)File: newt.c, Rev: 1.9, Date: 99/03/18 13:12:08, Author: Brian Schmidt"
|
||
|
|
||
|
/*============================== INCLUDE FILES ==============================*/
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <X11/Xlib.h>
|
||
|
#include <newtvideo.h>
|
||
|
#include <nvsession.h>
|
||
|
#include <macros.h>
|
||
|
#include <signal.h>
|
||
|
#include <thread.h>
|
||
|
#include <sched.h>
|
||
|
#include "newt.h"
|
||
|
|
||
|
/*================================ CONSTANTS ================================*/
|
||
|
|
||
|
/*================================= MACROS ==================================*/
|
||
|
|
||
|
/*============================ EXPORTED GLOBALS =============================*/
|
||
|
int yuv_dpcm = FALSE; /* use 4-bit DPCM on luma vals */
|
||
|
int yuv_subx = DEF_YUV_XSUBSAMP; /* horizontal chroma subsample */
|
||
|
int yuv_suby = DEF_YUV_YSUBSAMP; /* vertical chroma subsample */
|
||
|
int req_max_bw = TRUE; /* request max BW to NewT */
|
||
|
int newt_initialize_next = 1;
|
||
|
|
||
|
/*============================= STATIC GLOBALS ==============================*/
|
||
|
static ndga_t *ndga = NULL; /* NewT DGA interface */
|
||
|
static char *target = NULL; /* NewT target */
|
||
|
static nvSession nvsession = NULL; /* NewT session */
|
||
|
static nv_t newtnv = NULL; /* NewT video conn */
|
||
|
static u_char *line = NULL; /* line of pixels */
|
||
|
static struct { /* video state info */
|
||
|
int x, y; /* location */
|
||
|
int dw, dh; /* destination width/height */
|
||
|
int sw, sh; /* source width/height */
|
||
|
int clipEvent; /* clip event type */
|
||
|
NRegion clip; /* clip info */
|
||
|
} vinfo;
|
||
|
static u_char ymap[256]; /* color maps */
|
||
|
static u_char umap[256];
|
||
|
static u_char vmap[256];
|
||
|
|
||
|
/*============================= PREDECLARATIONS =============================*/
|
||
|
static void setup_video(Display *disp, Window win);
|
||
|
static void nvSessionCheck(nvSession sess);
|
||
|
|
||
|
extern char display_name[];
|
||
|
|
||
|
|
||
|
/*============================ EXPORTED ROUTINES ============================*/
|
||
|
|
||
|
void newt_set_palette( unsigned char *palette )
|
||
|
{
|
||
|
int i, j;
|
||
|
int y, u, v;
|
||
|
int r, g, b;
|
||
|
|
||
|
for (i = j = 0 ; i < 256 ; i++) {
|
||
|
/* get the RGB pixel values */
|
||
|
b = palette[i*4 + 2];
|
||
|
g = palette[i*4 + 1];
|
||
|
r = palette[i*4 + 0];
|
||
|
|
||
|
/* convert to YUV */
|
||
|
y = (77*r + 150*g + 29*b)/256;
|
||
|
u = (-44*r - 87*g + 131*b)/256 + 128;
|
||
|
v = (131*r - 110*g - 21*b)/256 + 128;
|
||
|
|
||
|
/* clamp the values */
|
||
|
ymap[i] = CLAMP(y);
|
||
|
umap[i] = CLAMP(u);
|
||
|
vmap[i] = CLAMP(v);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*========================= newt_process_event =========================
|
||
|
* Process events to watch for window changes.
|
||
|
*/
|
||
|
void newt_process_event(XEvent *xev)
|
||
|
{
|
||
|
if (ndga != NULL) ndga_process_event(ndga, xev);
|
||
|
}
|
||
|
|
||
|
/*========================= send_newt_video =========================
|
||
|
* Given an image consisting of 8-bit color index pixels, convert to
|
||
|
* YUV format and send to a NewT for scaling and display. The width
|
||
|
* and height are for the source image. Return 1 on success,
|
||
|
* 0 if we could not blast straight to the NewT screen.
|
||
|
*/
|
||
|
int send_newt_video(Display *disp, Window win,
|
||
|
void *data, int sw, int sh)
|
||
|
{
|
||
|
int x, y, w, h; /* window coordinates */
|
||
|
register int i, j, k, l, m; /* loop indexes */
|
||
|
u_char *frame = (u_char *)data; /* 8-bit pix for frame */
|
||
|
tile_t tile; /* video tile */
|
||
|
u_char *ydata, *udata, *vdata; /* yuv data */
|
||
|
int tx, ty, tw, th; /* tile location/size */
|
||
|
int ret = 1; /* return code */
|
||
|
int is_unobscured; /* true if window is unobscured */
|
||
|
int clipEvent; /* clip event type */
|
||
|
|
||
|
|
||
|
/* Set things up on the first time through */
|
||
|
if( newt_initialize_next ) {
|
||
|
newt_initialize_next = FALSE;
|
||
|
if( ndga != NULL ) {
|
||
|
ndga_destroy( ndga );
|
||
|
ndga = NULL;
|
||
|
}
|
||
|
memset( &vinfo, 0, sizeof( vinfo ) );
|
||
|
setup_video( disp, win );
|
||
|
}
|
||
|
|
||
|
/* make sure we have a conn to the NewT and can
|
||
|
* draw straight to the screen.
|
||
|
*/
|
||
|
nvSessionCheck(nvsession);
|
||
|
if ((target == NULL) ||
|
||
|
(!ndga_start(ndga, &x, &y, &w, &h, &is_unobscured, &clipEvent))) {
|
||
|
return 0;
|
||
|
}
|
||
|
if ((newtnv == NULL) ||
|
||
|
(vinfo.x != x) || (vinfo.y != y) ||
|
||
|
(vinfo.dw != w) || (vinfo.dh != h) ||
|
||
|
(vinfo.sw != sw) || (vinfo.sh != sh)) {
|
||
|
|
||
|
vinfo.x = x;
|
||
|
vinfo.y = y;
|
||
|
vinfo.sw = sw;
|
||
|
vinfo.sh = sh;
|
||
|
vinfo.dw = w;
|
||
|
vinfo.dh = h;
|
||
|
|
||
|
if (line != NULL) free(line);
|
||
|
if ((line = (u_char *)malloc(sw)) == NULL) {
|
||
|
fprintf(stderr, "send_newt_video: malloc faild\n");
|
||
|
ret = 0;
|
||
|
goto send_done;
|
||
|
}
|
||
|
|
||
|
if (newtnv != NULL) nv_destroy(newtnv);
|
||
|
if ((newtnv = nv_new(target, x, y, w, h, yuv_dpcm)) == NULL) {
|
||
|
ret = 0;
|
||
|
goto send_done;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* set up the clip region if necessary */
|
||
|
if (clipEvent != vinfo.clipEvent) {
|
||
|
vinfo.clip = ndga_clip(ndga, vinfo.clip);
|
||
|
nv_set_clip(newtnv, vinfo.clip);
|
||
|
}
|
||
|
|
||
|
/* Set up the video tiles */
|
||
|
if (nv_setup(newtnv, sw, sh, yuv_subx, yuv_suby) == 0) {
|
||
|
ret = 0;
|
||
|
goto send_done;
|
||
|
}
|
||
|
if (req_max_bw) nv_bwreq(newtnv, sw * sh * 3 * 40);
|
||
|
|
||
|
/* Convert 8-bit colormap data to yuv data. Copy or compress
|
||
|
* data into each tile
|
||
|
*/
|
||
|
tile = nv_next_tile(newtnv, TRUE,
|
||
|
&ydata, &udata, &vdata, &tx, &ty, &tw, &th);
|
||
|
while (tile != NULL) {
|
||
|
/* luma data */
|
||
|
for (j = 0 ; j < th ; j++) {
|
||
|
if (yuv_dpcm) {
|
||
|
int ywidth = (tw + 1) / 2;
|
||
|
|
||
|
for (i = 0 ; i < tw ; i++) {
|
||
|
line[i] = ymap[frame[(ty + j)*sw + tx + i]];
|
||
|
}
|
||
|
nv_compress(ydata, line, ywidth);
|
||
|
ydata += ywidth;
|
||
|
} else {
|
||
|
for (i = 0 ; i < tw ; i++) {
|
||
|
ydata[j*tw + i] =
|
||
|
ymap[frame[(ty + j)*sw + tx + i]];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* chroma data */
|
||
|
if ((yuv_subx > 0) && (yuv_suby > 0)) {
|
||
|
int uvx = tx/yuv_subx, uvy = ty/yuv_suby,
|
||
|
uvw = (tw + yuv_subx - 1)/yuv_subx,
|
||
|
uvh = (th + yuv_suby - 1)/yuv_suby;
|
||
|
|
||
|
for (i = j = 0 ; j < uvh ; j++) {
|
||
|
for (k = 0 ; k < uvw ; k++) {
|
||
|
u_int u = 0, v = 0;
|
||
|
|
||
|
for (l = 0 ; l < yuv_suby ; l++) {
|
||
|
for (m = 0 ; m < yuv_subx ; m++) {
|
||
|
u_int pel = frame[(ty + j*yuv_suby + l)*sw +
|
||
|
tx + k*yuv_subx + m];
|
||
|
|
||
|
u += umap[pel];
|
||
|
v += vmap[pel];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
udata[i] = u / (yuv_subx * yuv_suby);
|
||
|
vdata[i] = v / (yuv_subx * yuv_suby);
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* ship it off */
|
||
|
nv_send_tile(newtnv, tile);
|
||
|
|
||
|
/* get next tile */
|
||
|
tile = nv_next_tile(newtnv, FALSE,
|
||
|
&ydata, &udata, &vdata, &tx, &ty, &tw, &th);
|
||
|
}
|
||
|
|
||
|
send_done: /* leave */
|
||
|
ndga_done(ndga);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*============================= LOCAL ROUTINES ==============================*/
|
||
|
|
||
|
/*========================= setup_video =========================
|
||
|
* Setup a connection to a NewT so we can send video.
|
||
|
*/
|
||
|
static void setup_video(Display *disp, Window win)
|
||
|
{
|
||
|
char *session;
|
||
|
|
||
|
printf("Using NewT video: %d-bit luma, chroma subsamp W/%d H/%d\n",
|
||
|
yuv_dpcm ? 4 : 8, yuv_subx, yuv_suby);
|
||
|
|
||
|
/* Get the NewT target */
|
||
|
session = nvGetSessionId( display_name );
|
||
|
if ((session != NULL) && ((nvsession = nvSessionSetup(session)) != NULL)) {
|
||
|
struct sigaction sa;
|
||
|
|
||
|
target = NULL;
|
||
|
sa.sa_handler = exit;
|
||
|
if ((sigaction(SIGINT, &sa, NULL) < 0) ||
|
||
|
(sigaction(SIGHUP, &sa, NULL) < 0) ||
|
||
|
(sigaction(SIGTERM, &sa, NULL) < 0)) {
|
||
|
perror("sigaction");
|
||
|
newt_cleanup();
|
||
|
exit(1);
|
||
|
}
|
||
|
/* atexit(newt_cleanup);*/
|
||
|
} else {
|
||
|
nvsession = NULL;
|
||
|
target = getenv("NEWT_TARGET");
|
||
|
}
|
||
|
|
||
|
if ((nvsession == NULL) && (target == NULL)) {
|
||
|
Sys_Error("Could not open connection to NewT");
|
||
|
}
|
||
|
|
||
|
/* Create NewT DGA context */
|
||
|
ndga = ndga_new(disp, win, nvsession, 1, 1);
|
||
|
}
|
||
|
|
||
|
/*========================= newt_cleanup =========================
|
||
|
* Clean up on exit. Kill the NewT video session.
|
||
|
*/
|
||
|
void newt_cleanup( void )
|
||
|
{
|
||
|
if( ndga != NULL ) {
|
||
|
ndga_destroy( ndga );
|
||
|
}
|
||
|
|
||
|
if( newtnv != NULL ) {
|
||
|
nv_destroy(newtnv);
|
||
|
newtnv = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*========================= nvSessionCheck =========================
|
||
|
* Check the status of the given NewT session and set the target.
|
||
|
*/
|
||
|
static void nvSessionCheck(nvSession sess)
|
||
|
{
|
||
|
char *new_target;
|
||
|
register int i;
|
||
|
|
||
|
if (sess == NULL) return;
|
||
|
|
||
|
new_target = nvSessionTarget(sess);
|
||
|
|
||
|
/* Status has changed; break everything down and start again */
|
||
|
if ((new_target==NULL) || (target==NULL) || strcmp(new_target,target)) {
|
||
|
if (newtnv != NULL) {
|
||
|
/* Lost the connection. Destroy it */
|
||
|
nv_destroy(newtnv);
|
||
|
newtnv = NULL;
|
||
|
}
|
||
|
|
||
|
/* Set new target */
|
||
|
if (target) {
|
||
|
free(target);
|
||
|
target = NULL;
|
||
|
}
|
||
|
if (new_target) target = strdup(new_target);
|
||
|
}
|
||
|
}
|