-/* analogtv, Copyright (c) 2003 Trevor Blackwell <tlb@tlb.org>
+/* analogtv, Copyright (c) 2003, 2004 Trevor Blackwell <tlb@tlb.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
Trevor Blackwell <tlb@tlb.org>
*/
-#include <X11/Xutil.h>
-#include <X11/Intrinsic.h>
+#ifdef HAVE_COCOA
+# include "jwxyz.h"
+#else /* !HAVE_COCOA */
+# include <X11/Xlib.h>
+# include <X11/Xutil.h>
+#endif
+
#include <assert.h>
#include "utils.h"
#include "resources.h"
char buf[256];
sprintf(buf,"%sTVTint",prefix);
- it->tint_control = get_float_resource(buf,"TVTint");
+ it->tint_control = get_float_resource(it->dpy, buf,"TVTint");
sprintf(buf,"%sTVColor",prefix);
- it->color_control = get_float_resource(buf,"TVColor")/100.0;
+ it->color_control = get_float_resource(it->dpy, buf,"TVColor")/100.0;
sprintf(buf,"%sTVBrightness",prefix);
- it->brightness_control = get_float_resource(buf,"TVBrightness") / 100.0;
+ it->brightness_control = get_float_resource(it->dpy, buf,"TVBrightness") / 100.0;
sprintf(buf,"%sTVContrast",prefix);
- it->contrast_control = get_float_resource(buf,"TVContrast") / 100.0;
+ it->contrast_control = get_float_resource(it->dpy, buf,"TVContrast") / 100.0;
it->height_control = 1.0;
it->width_control = 1.0;
it->squish_control = 0.0;
extern Bool mono_p; /* shoot me */
-void
+static void
analogtv_free_image(analogtv *it)
{
if (it->image) {
}
}
-void
+static void
analogtv_alloc_image(analogtv *it)
{
if (it->use_shm) {
if (!it->image) {
it->image = XCreateImage(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0, 0,
it->usewidth, it->useheight, 8, 0);
- it->image->data = (char *)calloc(it->image->height, it->image->bytes_per_line);
+ it->image->data = (char *)malloc(it->image->height * it->image->bytes_per_line);
}
+ memset (it->image->data, 0, it->image->height * it->image->bytes_per_line);
}
-void
+static void
analogtv_configure(analogtv *it)
{
int oldwidth=it->usewidth;
int oldheight=it->useheight;
- int wlim,hlim,ohlim;
+ int wlim,hlim,height_diff;
+
+ /* If the window is very small, don't let the image we draw get lower
+ than the actual TV resolution (266x200.)
- hlim=it->xgwa.height;
- if (hlim<ANALOGTV_VISLINES) hlim = ANALOGTV_VISLINES;
+ If the aspect ratio of the window is within 15% of a 4:3 ratio,
+ then scale the image to exactly fill the window.
+ Otherwise, center the image either horizontally or vertically,
+ padding on the left+right, or top+bottom, but not both.
+
+ If it's very close (2.5%) to a multiple of VISLINES, make it exact
+ For example, it maps 1024 => 1000.
+ */
+ float percent = 0.15; /* jwz: 20% caused severe top/bottom clipping
+ in Pong on 1680x1050 iMac screen. */
+ float min_ratio = 4.0 / 3.0 * (1 - percent);
+ float max_ratio = 4.0 / 3.0 * (1 + percent);
+ float ratio;
+ float height_snap=0.025;
+
+ hlim = it->xgwa.height;
wlim = it->xgwa.width;
- if (wlim<300) wlim = 300;
+ ratio = wlim / (float) hlim;
- /* require 3:4 aspect ratio */
- if (wlim > hlim*4/3) wlim=hlim*4/3;
- if (hlim > wlim*3/4) hlim=wlim*3/4;
+ if (wlim < 266 || hlim < 200)
+ {
+ wlim = 266;
+ hlim = 200;
+# ifdef DEBUG
+ fprintf (stderr,
+ "size: minimal: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
+ wlim, hlim, it->xgwa.width, it->xgwa.height,
+ min_ratio, ratio, max_ratio);
+# endif
+ }
+ else if (ratio > min_ratio && ratio < max_ratio)
+ {
+# ifdef DEBUG
+ fprintf (stderr,
+ "size: close enough: %dx%d (%.3f < %.3f < %.3f)\n",
+ wlim, hlim, min_ratio, ratio, max_ratio);
+# endif
+ }
+ else if (ratio > max_ratio)
+ {
+ wlim = hlim*max_ratio;
+# ifdef DEBUG
+ fprintf (stderr,
+ "size: center H: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
+ wlim, hlim, it->xgwa.width, it->xgwa.height,
+ min_ratio, ratio, max_ratio);
+# endif
+ }
+ else /* ratio < min_ratio */
+ {
+ hlim = wlim/min_ratio;
+# ifdef DEBUG
+ fprintf (stderr,
+ "size: center V: %dx%d in %dx%d (%.3f < %.3f < %.3f)\n",
+ wlim, hlim, it->xgwa.width, it->xgwa.height,
+ min_ratio, ratio, max_ratio);
+# endif
+ }
- /* height must be a multiple of VISLINES */
- ohlim=hlim;
- hlim = (hlim/ANALOGTV_VISLINES)*ANALOGTV_VISLINES;
- /* Scale width proportionally */
- wlim=wlim*hlim/ohlim;
+ height_diff = ((hlim + ANALOGTV_VISLINES/2) % ANALOGTV_VISLINES) - ANALOGTV_VISLINES/2;
+ if (height_diff != 0 && fabs(height_diff) < hlim * height_snap)
+ {
+ hlim -= height_diff;
+ }
- {
- FILE *fp=fopen("/tmp/analogtv.size","w");
- fprintf(fp,"wlim=%d hlim=%d\n", wlim, hlim);
- fclose(fp);
- }
/* Most times this doesn't change */
if (wlim != oldwidth || hlim != oldheight) {
it->visbits=it->xgwa.visual->bits_per_rgb;
it->visdepth=it->xgwa.depth;
if (it->visclass == TrueColor || it->visclass == DirectColor) {
- if (get_integer_resource ("use_cmap", "Integer")) {
+ if (get_integer_resource (it->dpy, "use_cmap", "Integer")) {
it->use_cmap=1;
} else {
it->use_cmap=0;
/* Is there a standard way to do this? Does this handle all cases? */
int shift, prec;
for (shift=0; shift<32; shift++) {
- for (prec=1; prec<16 && prec<32-shift; prec++) {
+ for (prec=1; prec<16 && prec<40-shift; prec++) {
unsigned long mask=(0xffffUL>>(16-prec)) << shift;
if (it->red_shift<0 && mask==it->red_mask)
it->red_shift=shift, it->red_invprec=16-prec;
}
- gcv.background=get_pixel_resource("background", "Background",
- it->dpy, it->colormap);
+ gcv.background=get_pixel_resource(it->dpy, it->colormap,
+ "background", "Background");
it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
XSetWindowBackground(it->dpy, it->window, gcv.background);
it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
}
- if (it->hashnoise_rpm >= 0.0) {
+ if (it->hashnoise_rpm > 0.0) {
int hni;
int hnc=it->hashnoise_counter; /* in 24.8 format */
hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;
}
- it->agclevel = 1.0/it->rx_signal_level;
+ if (it->rx_signal_level != 0.0)
+ it->agclevel = 1.0/it->rx_signal_level;
#ifdef DEBUG2
analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
{
int i,lineno,vsync;
- char *sig;
+ signed char *sig;
int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
}
}
-void
+static void
analogtv_sync(analogtv *it)
{
int cur_hsync=it->cur_hsync;
int cur_vsync=it->cur_vsync;
- int lineno;
+ int lineno = 0;
int i,j;
double osc,filt;
double *sp;
static double
analogtv_levelmult(analogtv *it, int level)
{
- static double levelfac[3]={-7.5, 5.5, 24.5};
+ static const double levelfac[3]={-7.5, 5.5, 24.5};
return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
}
return level;
}
+/*
+ The point of this stuff is to ensure that when useheight is not a
+ multiple of VISLINES so that TV scan lines map to different numbers
+ of vertical screen pixels, the total brightness of each scan line
+ remains the same.
+ ANALOGTV_MAX_LINEHEIGHT corresponds to 2400 vertical pixels, beyond which
+ it interpolates extra black lines.
+ */
+
+static void
+analogtv_setup_levels(analogtv *it, double avgheight)
+{
+ int i,height;
+ static const double levelfac[3]={-7.5, 5.5, 24.5};
+
+ for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
+
+ for (i=0; i<height; i++) {
+ it->leveltable[height][i].index = 2;
+ }
+
+ if (avgheight>=3) {
+ it->leveltable[height][0].index=0;
+ }
+ if (avgheight>=5) {
+ it->leveltable[height][height-1].index=0;
+ }
+ if (avgheight>=7) {
+ it->leveltable[height][1].index=1;
+ it->leveltable[height][height-2].index=1;
+ }
+
+ for (i=0; i<height; i++) {
+ it->leveltable[height][i].value =
+ (40.0 + levelfac[it->leveltable[height][i].index]*puramp(it, 3.0, 6.0, 1.0)) / 256.0;
+ }
+
+ }
+}
+
static void
analogtv_blast_imagerow(analogtv *it,
float *rgbf, float *rgbf_end,
for (i=0; i<3; i++) level_copyfrom[i]=NULL;
for (y=ytop; y<ybot; y++) {
- int level=analogtv_level(it, y, ytop, ybot);
+ int level=it->leveltable[ybot-ytop][y-ytop].index;
+ double levelmult=it->leveltable[ybot-ytop][y-ytop].value;
char *rowdata;
rowdata=it->image->data + y*it->image->bytes_per_line;
memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
}
else {
- double levelmult=analogtv_levelmult(it, level);
level_copyfrom[level] = rowdata;
if (0) {
puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
(1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
+ analogtv_setup_levels(it, puheight * (double)it->useheight/(double)ANALOGTV_VISLINES);
+
overall_top=it->useheight;
overall_bot=0;
if (ytop<0) ytop=0;
if (ybot>it->useheight) ybot=it->useheight;
+ if (ybot > ytop+ANALOGTV_MAX_LINEHEIGHT) ybot=ytop+ANALOGTV_MAX_LINEHEIGHT;
+
if (ytop < overall_top) overall_top=ytop;
if (ybot > overall_bot) overall_bot=ybot;
it->last_display_time=tv;
}
#endif
-
- XSync(it->dpy,0);
}
analogtv_input *
XColor col1[ANALOGTV_PIC_LEN];
XColor col2[ANALOGTV_PIC_LEN];
int multiq[ANALOGTV_PIC_LEN+4];
+ int y_overscan=5; /* overscan this much top and bottom */
+ int y_scanlength=ANALOGTV_VISLINES+2*y_overscan;
img_w=pic_im->width;
img_h=pic_im->height;
-
+
for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
double phase=90.0-90.0*i;
double ampl=1.0;
multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
}
- for (y=0; y<ANALOGTV_VISLINES; y++) {
- int picy1=(y*img_h)/ANALOGTV_VISLINES;
- int picy2=(y*img_h+ANALOGTV_VISLINES/2)/ANALOGTV_VISLINES;
+ for (y=0; y<y_scanlength; y++) {
+ int picy1=(y*img_h)/y_scanlength;
+ int picy2=(y*img_h + y_scanlength/2)/y_scanlength;
for (x=0; x<ANALOGTV_PIC_LEN; x++) {
int picx=(x*img_w)/ANALOGTV_PIC_LEN;
composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
if (composite>125) composite=125;
if (composite<0) composite=0;
- input->signal[y+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
+ input->signal[y-y_overscan+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
}
}
double *ps=it->rx_signal;
double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN;
double *p=ps;
- char *ss=&inp->signal[0][0];
- char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
- char *s=ss + ((unsigned)rec->ofs % ANALOGTV_SIGNAL_LEN);
+ signed char *ss=&inp->signal[0][0];
+ signed char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
+ signed char *s=ss + ((unsigned)rec->ofs % ANALOGTV_SIGNAL_LEN);
int i;
int ec=it->channel_change_cycles;
double level=rec->level;
}
+/* jwz: since MacOS doesn't have "6x10", I dumped this font to an XBM...
+ */
+
+#include "images/6x10font.xbm"
+
void
analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
int w, int h, char *fontname)
XGetWindowAttributes (dpy, window, &xgwa);
- if (fontname) {
+ if (fontname && !strcmp (fontname, "6x10")) {
+
+ text_pm = XCreatePixmapFromBitmapData (dpy, window,
+ (char *) font6x10_bits,
+ font6x10_width,
+ font6x10_height,
+ 1, 0, 1);
+ f->text_im = XGetImage(dpy, text_pm, 0, 0, font6x10_width, font6x10_height,
+ 1, XYPixmap);
+ XFreePixmap(dpy, text_pm);
+
+ } else if (fontname) {
font = XLoadQueryFont (dpy, fontname);
if (!font) {
abort();
}
- text_pm=XCreatePixmap(dpy, window, 128*f->char_w, f->char_h, xgwa.depth);
+ text_pm=XCreatePixmap(dpy, window, 256*f->char_w, f->char_h, 1);
memset(&gcv, 0, sizeof(gcv));
gcv.foreground=1;
gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
XSetForeground(dpy, gc, 0);
- XFillRectangle(dpy, text_pm, gc, 0, 0, 128*f->char_w, f->char_h);
+ XFillRectangle(dpy, text_pm, gc, 0, 0, 256*f->char_w, f->char_h);
XSetForeground(dpy, gc, 1);
- /* Just ASCII */
- for (i=0; i<128; i++) {
+ for (i=0; i<256; i++) {
char c=i;
int x=f->char_w*i+1;
int y=f->char_h*8/10;
XDrawString(dpy, text_pm, gc, x, y, &c, 1);
}
- f->text_im = XGetImage(dpy, text_pm, 0, 0, 128*f->char_w, f->char_h,
- ~0L, ZPixmap);
+ f->text_im = XGetImage(dpy, text_pm, 0, 0, 256*f->char_w, f->char_h,
+ 1, XYPixmap);
+# if 0
+ XWriteBitmapFile(dpy, "/tmp/tvfont.xbm", text_pm,
+ 256*f->char_w, f->char_h, -1, -1);
+# endif
XFreeGC(dpy, gc);
XFreePixmap(dpy, text_pm);
} else {
- f->text_im = XCreateImage(dpy, xgwa.visual, xgwa.depth,
- ZPixmap, 0, 0,
- 128*f->char_w, f->char_h, 8, 0);
+ f->text_im = XCreateImage(dpy, xgwa.visual, 1, XYPixmap, 0, 0,
+ 256*f->char_w, f->char_h, 8, 0);
f->text_im->data = (char *)calloc(f->text_im->height,
f->text_im->bytes_per_line);
{
if (x<0 || x>=f->char_w) return 0;
if (y<0 || y>=f->char_h) return 0;
- if (c<0 || c>=128) return 0;
+ if (c<0 || c>=256) return 0;
return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
}
{
if (x<0 || x>=f->char_w) return;
if (y<0 || y>=f->char_h) return;
- if (c<0 || c>=128) return;
+ if (c<0 || c>=256) return;
XPutPixel(f->text_im, c*f->char_w + x, y, value);
}
{
int value,x,y;
- if (c<0 || c>=128) return;
+ if (c<0 || c>=256) return;
for (y=0; y<f->char_h; y++) {
for (x=0; x<f->char_w; x++) {
}
}
}
-
-extern XtAppContext app;
-
-int
-analogtv_handle_events (analogtv *it)
-{
- XSync(it->dpy, False);
- if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
- XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
-
- while (XPending (it->dpy))
- {
- XEvent event;
- XNextEvent (it->dpy, &event);
- switch (event.xany.type)
- {
- case ButtonPress:
- return 1;
-
- case KeyPress:
- {
- KeySym keysym;
- char c = 0;
- XLookupString (&event.xkey, &c, 1, &keysym, 0);
- if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
- return 1;
- }
- break;
-
- /* I don't seem to get an event when clicking the "full
- screen" window manager icon, at least when using
- metacity. Thus, it doesn't change the video size. Is this
- some separate WM_* message I have to deal with?
- */
- case ConfigureNotify:
- if (event.xconfigure.width != it->xgwa.width ||
- event.xconfigure.height != it->xgwa.height)
- analogtv_reconfigure(it);
- break;
-
- case Expose:
- case GraphicsExpose:
- it->need_clear=1;
- break;
-
- default:
- break;
- }
- if (it->event_handler) {
- (*it->event_handler) (it->dpy, &event);
- }
- }
- return 0;
-}
-