*
*/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
#include <math.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifndef HAVE_COCOA
+# include <X11/Intrinsic.h> /* for XtDatabase in hack_resources() */
+#endif
+
#include "screenhack.h"
#include "xpm-pixmap.h"
#include "analogtv.h"
-#include <stdio.h>
-#include <time.h>
-#include <sys/time.h>
-#include <X11/Xutil.h>
-#include <X11/Intrinsic.h>
#include "images/logo-50.xpm"
#define countof(x) (sizeof((x))/sizeof((*x)))
-static analogtv *tv=NULL;
+enum {
+ N_CHANNELS=12, /* Channels 2 through 13 on VHF */
+ MAX_MULTICHAN=2,
+ MAX_STATIONS=6
+};
+
+typedef struct chansetting_s {
+
+ analogtv_reception recs[MAX_MULTICHAN];
+ double noise_level;
+ Bool image_loaded_p;
+/* char *filename; was only used for diagnostics */
+ int dur;
+} chansetting;
+
+
+struct state {
+ Display *dpy;
+ Window window;
+ analogtv *tv;
+ analogtv_font ugly_font;
+ struct timeval basetime;
+
+ int n_stations;
+ analogtv_input *stations[MAX_STATIONS];
+ Bool image_loading_p;
+
+ int curinputi;
+ int change_ticks;
+ chansetting chansettings[N_CHANNELS];
+ chansetting *cs;
+
+ int change_now;
+
+};
-analogtv_font ugly_font;
static void
update_smpte_colorbars(analogtv_input *input)
{
+ struct state *st = (struct state *) input->client_data;
int col;
int xpos, ypos;
int black_ntsc[4];
space is provided" */
localname[24]=0; /* limit length */
- analogtv_draw_string_centered(input, &ugly_font, localname,
+ analogtv_draw_string_centered(input, &st->ugly_font, localname,
xpos, ypos, black_ntsc);
}
}
- ypos += ugly_font.char_h*5/2;
+ ypos += st->ugly_font.char_h*5/2;
- analogtv_draw_xpm(tv, input,
+ analogtv_draw_xpm(st->tv, input,
logo_50_xpm, xpos - 100, ypos);
ypos += 58;
#if 0
- analogtv_draw_string_centered(input, &ugly_font, "Please Stand By", xpos, ypos);
- ypos += ugly_font.char_h*4;
+ analogtv_draw_string_centered(input, &st->ugly_font,
+ "Please Stand By", xpos, ypos);
+ ypos += st->ugly_font.char_h*4;
#endif
{
/* Y2K: It is OK for this to use a 2-digit year because it's simulating a
TV display and is purely decorative. */
strftime(timestamp, sizeof(timestamp)-1, "%y.%m.%d %H:%M:%S ", tm);
- analogtv_draw_string_centered(input, &ugly_font, timestamp,
+ analogtv_draw_string_centered(input, &st->ugly_font, timestamp,
xpos, ypos, black_ntsc);
}
}
#endif
-char *progclass = "XAnalogTV";
-
-char *defaults [] = {
- "*delay: 5",
+static const char *xanalogtv_defaults [] = {
+ ".background: black",
+ ".foreground: white",
+ "*delay: 5",
+ "*grabDesktopImages: False", /* HAVE_COCOA */
+ "*chooseRandomImages: True", /* HAVE_COCOA */
ANALOGTV_DEFAULTS
0,
};
-XrmOptionDescRec options [] = {
+static XrmOptionDescRec xanalogtv_options [] = {
{ "-delay", ".delay", XrmoptionSepArg, 0 },
ANALOGTV_OPTIONS
{ 0, 0, 0, 0 }
#endif
-enum {
- N_CHANNELS=12, /* Channels 2 through 13 on VHF */
- MAX_MULTICHAN=2
-};
+static int
+getticks(struct state *st)
+{
+ struct timeval tv;
+ gettimeofday(&tv,NULL);
+ return ((tv.tv_sec - st->basetime.tv_sec)*1000 +
+ (tv.tv_usec - st->basetime.tv_usec)/1000);
+}
-typedef struct chansetting_s {
- analogtv_reception recs[MAX_MULTICHAN];
- double noise_level;
+/* The first time we grab an image, do it the default way.
+ The second and subsequent times, add "-no-desktop" to the command.
+ That way we don't have to watch the window un-map 5+ times in a row.
+ Also, we end up with the desktop on only one channel, and pictures
+ on all the others (or colorbars, if no imageDirectory is set.)
+ */
+static void
+hack_resources (Display *dpy)
+{
+#ifndef HAVE_COCOA
+ static int count = -1;
+ count++;
+
+ if (count == 0)
+ return;
+ else if (count == 1)
+ {
+ XrmDatabase db = XtDatabase (dpy);
+ char *res = "desktopGrabber";
+ char *val = get_string_resource (dpy, res, "DesktopGrabber");
+ char buf1[255];
+ char buf2[255];
+ XrmValue value;
+ sprintf (buf1, "%.100s.%.100s", progname, res);
+ sprintf (buf2, "%.200s -no-desktop", val);
+ value.addr = buf2;
+ value.size = strlen(buf2);
+ XrmPutResource (&db, buf1, "String", &value);
+ }
+#endif /* HAVE_COCOA */
+}
- int dur;
-} chansetting;
-static struct timeval basetime;
+static void analogtv_load_random_image(struct state *);
-static int
-getticks(void)
+
+static void image_loaded_cb (Screen *screen, Window window, Drawable pixmap,
+ const char *name, XRectangle *geometry,
+ void *closure)
{
- struct timeval tv;
- gettimeofday(&tv,NULL);
- return ((tv.tv_sec - basetime.tv_sec)*1000 +
- (tv.tv_usec - basetime.tv_usec)/1000);
+ /* When an image has just been loaded, store it into the first available
+ channel. If there are other unloaded channels, then start loading
+ another image.
+ */
+ struct state *st = (struct state *) closure;
+ int i;
+ int this = -1;
+ int next = -1;
+
+ if (!st->image_loading_p) abort(); /* only one at a time... */
+ st->image_loading_p = False;
+
+ for (i = 0; i < MAX_STATIONS; i++) {
+ if (! st->chansettings[i].image_loaded_p) {
+ if (this == -1) this = i;
+ else if (next == -1) next = i;
+ }
+ }
+ if (this == -1) abort(); /* no unloaded stations? */
+
+ /* Load this image into the next channel. */
+ {
+ analogtv_input *input = st->stations[this];
+ int width=ANALOGTV_PIC_LEN;
+ int height=width*3/4;
+ XImage *image = XGetImage (st->dpy, pixmap, 0, 0,
+ width, height, ~0L, ZPixmap);
+ XFreePixmap(st->dpy, pixmap);
+
+ analogtv_setup_sync(input, 1, (random()%20)==0);
+ analogtv_load_ximage(st->tv, input, image);
+ if (image) XDestroyImage(image);
+ st->chansettings[this].image_loaded_p = True;
+#if 0
+ if (name) {
+ const char *s = strrchr (name, '/');
+ if (s) s++;
+ else s = name;
+ st->chansettings[this].filename = strdup (s);
+ }
+ fprintf(stderr, "%s: loaded channel %d, %s\n", progname, this,
+ st->chansettings[this].filename);
+#endif
+ }
+
+ /* If there are still unloaded stations, fire off another loader. */
+ if (next != -1)
+ analogtv_load_random_image (st);
}
-int
-analogtv_load_random_image(analogtv *it, analogtv_input *input)
+
+/* Queues a single image for loading. Only load one at a time.
+ The image is done loading when st->img_loader is null and
+ it->loaded_image is a pixmap.
+ */
+static void
+analogtv_load_random_image(struct state *st)
{
- Pixmap pixmap;
- XImage *image=NULL;
int width=ANALOGTV_PIC_LEN;
int height=width*3/4;
- int rc;
+ Pixmap p;
- pixmap=XCreatePixmap(it->dpy, it->window, width, height, it->visdepth);
- XSync(it->dpy, False);
- load_random_image(it->screen, it->window, pixmap, NULL);
- image = XGetImage(it->dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap);
- XFreePixmap(it->dpy, pixmap);
-
- /* Make sure the window's background is not set to None, and get the
- grabbed bits (if any) off it as soon as possible. */
- XSetWindowBackground (it->dpy, it->window,
- get_pixel_resource ("background", "Background",
- it->dpy, it->xgwa.colormap));
- XClearWindow (it->dpy, it->window);
+ if (st->image_loading_p) /* a load is already in progress */
+ return;
- analogtv_setup_sync(input, 1, (random()%20)==0);
- rc=analogtv_load_ximage(it, input, image);
- if (image) XDestroyImage(image);
- XSync(it->dpy, False);
- return rc;
+ st->image_loading_p = True;
+ p = XCreatePixmap(st->dpy, st->window, width, height, st->tv->visdepth);
+ hack_resources(st->dpy);
+ load_image_async (st->tv->xgwa.screen, st->window, p, image_loaded_cb, st);
}
-int
+
+#if 0
+static int
analogtv_load_xpm(analogtv *it, analogtv_input *input, char **xpm)
{
Pixmap pixmap;
XFreePixmap(it->dpy, pixmap);
rc=analogtv_load_ximage(it, input, image);
if (image) XDestroyImage(image);
- XSync(it->dpy, False);
return rc;
}
-
-enum { MAX_STATIONS = 6 };
-static int n_stations;
-static analogtv_input *stations[MAX_STATIONS];
+#endif
-void add_stations(void)
+static void add_stations(struct state *st)
{
- while (n_stations < MAX_STATIONS) {
+ while (st->n_stations < MAX_STATIONS) {
analogtv_input *input=analogtv_input_allocate();
- stations[n_stations++]=input;
+ st->stations[st->n_stations++]=input;
+ input->client_data = st;
+ }
+}
- if (n_stations==1) {
+
+static void load_station_images(struct state *st)
+{
+ int i;
+ for (i = 0; i < MAX_STATIONS; i++) {
+ analogtv_input *input = st->stations[i];
+
+ st->chansettings[i].image_loaded_p = True;
+ if (i == 0) { /* station 0 is always colorbars */
input->updater = update_smpte_colorbars;
input->do_teletext=1;
}
}
#endif
else {
- analogtv_load_random_image(tv, input);
+ analogtv_load_random_image(st);
input->do_teletext=1;
+ st->chansettings[i].image_loaded_p = False;
}
}
}
-void
-screenhack (Display *dpy, Window window)
+
+static void *
+xanalogtv_init (Display *dpy, Window window)
{
+ struct state *st = (struct state *) calloc (1, sizeof(*st));
int i;
- int curinputi;
- int change_ticks;
- int using_mouse=0;
- int change_now;
- chansetting chansettings[N_CHANNELS];
- chansetting *cs;
int last_station=42;
- int delay = get_integer_resource("delay", "Integer");
+ int delay = get_integer_resource(dpy, "delay", "Integer");
if (delay < 1) delay = 1;
- analogtv_make_font(dpy, window, &ugly_font, 7, 10, "6x10");
+ analogtv_make_font(dpy, window, &st->ugly_font, 7, 10, "6x10");
- tv=analogtv_allocate(dpy, window);
- tv->event_handler = screenhack_handle_event;
+ st->dpy = dpy;
+ st->window = window;
+ st->tv=analogtv_allocate(dpy, window);
- add_stations();
+ add_stations(st);
- analogtv_set_defaults(tv, "");
- tv->need_clear=1;
+ analogtv_set_defaults(st->tv, "");
+ st->tv->need_clear=1;
if (random()%4==0) {
- tv->tint_control += pow(frand(2.0)-1.0, 7) * 180.0;
+ st->tv->tint_control += pow(frand(2.0)-1.0, 7) * 180.0;
}
if (1) {
- tv->color_control += frand(0.3);
+ st->tv->color_control += frand(0.3);
}
for (i=0; i<N_CHANNELS; i++) {
- memset(&chansettings[i], 0, sizeof(chansetting));
+ memset(&st->chansettings[i], 0, sizeof(chansetting));
- chansettings[i].noise_level = 0.06;
- chansettings[i].dur = 1000*delay;
+ st->chansettings[i].noise_level = 0.06;
+ st->chansettings[i].dur = 1000*delay;
if (random()%6==0) {
- chansettings[i].dur=600;
+ st->chansettings[i].dur=600;
}
else {
int stati;
for (stati=0; stati<MAX_MULTICHAN; stati++) {
- analogtv_reception *rec=&chansettings[i].recs[stati];
+ analogtv_reception *rec=&st->chansettings[i].recs[stati];
int station;
while (1) {
- station=random()%n_stations;
+ station=random()%st->n_stations;
if (station!=last_station) break;
if ((random()%10)==0) break;
}
last_station=station;
- rec->input = stations[station];
+ rec->input = st->stations[station];
rec->level = pow(frand(1.0), 3.0) * 2.0 + 0.05;
rec->ofs=random()%ANALOGTV_SIGNAL_LEN;
if (random()%3) {
}
}
- gettimeofday(&basetime,NULL);
+ gettimeofday(&st->basetime,NULL);
- curinputi=0;
- cs=&chansettings[curinputi];
- change_ticks = cs->dur + 1500;
+ st->curinputi=0;
+ st->cs = &st->chansettings[st->curinputi];
+ st->change_ticks = st->cs->dur + 1500;
- tv->powerup=0.0;
- while (1) {
- int curticks=getticks();
- double curtime=curticks*0.001;
+ st->tv->powerup=0.0;
- change_now=0;
- if (analogtv_handle_events(tv)) {
- using_mouse=1;
- change_now=1;
- }
- if (change_now || (!using_mouse && curticks>=change_ticks
- && tv->powerup > 10.0)) {
- curinputi=(curinputi+1)%N_CHANNELS;
- cs=&chansettings[curinputi];
- change_ticks = curticks + cs->dur;
- /* Set channel change noise flag */
- tv->channel_change_cycles=200000;
- }
+ load_station_images(st);
- for (i=0; i<MAX_MULTICHAN; i++) {
- analogtv_reception *rec=&cs->recs[i];
- analogtv_input *inp=rec->input;
- if (!inp) continue;
+ return st;
+}
- if (inp->updater) {
- inp->next_update_time = curtime;
- (inp->updater)(inp);
- }
- rec->ofs += rec->freqerr;
- }
+static unsigned long
+xanalogtv_draw (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ int i;
+
+ int curticks=getticks(st);
+ double curtime=curticks*0.001;
+
+ const analogtv_reception *recs[MAX_MULTICHAN];
+ unsigned rec_count = 0;
+
+ int auto_change = curticks >= st->change_ticks && st->tv->powerup > 10.0 ? 1 : 0;
+
+ if (st->change_now || auto_change) {
+ st->curinputi=(st->curinputi+st->change_now+auto_change+N_CHANNELS)%N_CHANNELS;
+ st->change_now = 0;
+ st->cs = &st->chansettings[st->curinputi];
+#if 0
+ fprintf (stderr, "%s: channel %d, %s\n", progname, st->curinputi,
+ st->cs->filename);
+#endif
+ st->change_ticks = curticks + st->cs->dur;
+ /* Set channel change noise flag */
+ st->tv->channel_change_cycles=200000;
+ }
- tv->powerup=curtime;
+ for (i=0; i<MAX_MULTICHAN; i++) {
+ analogtv_reception *rec = &st->cs->recs[i];
+ analogtv_input *inp=rec->input;
+ if (!inp) continue;
- analogtv_init_signal(tv, cs->noise_level);
- for (i=0; i<MAX_MULTICHAN; i++) {
- analogtv_reception *rec=&cs->recs[i];
- analogtv_input *inp=rec->input;
- if (!inp) continue;
+ if (inp->updater) {
+ inp->next_update_time = curtime;
+ (inp->updater)(inp);
+ }
+ rec->ofs += rec->freqerr;
+ }
+ st->tv->powerup=curtime;
+
+ for (i=0; i<MAX_MULTICHAN; i++) {
+ analogtv_reception *rec = &st->cs->recs[i];
+ if (rec->input) {
analogtv_reception_update(rec);
- analogtv_add_signal(tv, rec);
+ recs[rec_count] = rec;
+ ++rec_count;
}
- analogtv_draw(tv);
}
+ analogtv_draw(st->tv, st->cs->noise_level, recs, rec_count);
- XSync(dpy, False);
- XClearWindow(dpy, window);
-
- if (tv) analogtv_release(tv);
+#ifdef USE_IPHONE
+ return 0;
+#else
+ return 5000;
+#endif
+}
+
+static void
+xanalogtv_reshape (Display *dpy, Window window, void *closure,
+ unsigned int w, unsigned int h)
+{
+ struct state *st = (struct state *) closure;
+ analogtv_reconfigure(st->tv);
+}
+
+static Bool
+xanalogtv_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+ struct state *st = (struct state *) closure;
+
+ if (event->type == ButtonPress)
+ {
+ unsigned button = event->xbutton.button;
+ st->change_now = button == 2 || button == 3 || button == 5 ? -1 : 1;
+ return True;
+ }
+ else if (event->type == KeyPress)
+ {
+ KeySym keysym;
+ char c = 0;
+ XLookupString (&event->xkey, &c, 1, &keysym, 0);
+ if (c == ' ' || c == '\t' || c == '\r' || c == '\n' ||
+ keysym == XK_Up || keysym == XK_Right || keysym == XK_Prior)
+ {
+ st->change_now = 1;
+ return True;
+ }
+ else if (c == '\b' ||
+ keysym == XK_Down || keysym == XK_Left || keysym == XK_Next)
+ {
+ st->change_now = -1;
+ return True;
+ }
+ else if (screenhack_event_helper (dpy, window, event))
+ goto DEF;
+ }
+ else if (screenhack_event_helper (dpy, window, event))
+ {
+ DEF:
+ st->change_now = ((random() & 1) ? 1 : -1);
+ return True;
+ }
+
+ return False;
+}
+
+static void
+xanalogtv_free (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ analogtv_release(st->tv);
+ free (st);
}
+
+XSCREENSAVER_MODULE ("XAnalogTV", xanalogtv)