1 /* xanalogtv, Copyright (c) 2003 Trevor Blackwell <tlb@tlb.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 * Simulate test patterns on an analog TV. Concept similar to xteevee
13 * in this distribution, but a totally different implementation based
14 * on the simulation of an analog TV set in utils/analogtv.c. Much
15 * more realistic, but needs more video card bandwidth.
17 * It flips around through simulated channels 2 through 13. Some show
18 * pictures from your images directory, some show color bars, and some
19 * just have static. Some channels receive two stations simultaneously
20 * so you see a ghostly, misaligned image.
22 * It's easy to add some test patterns by compiling in an XPM, but I
23 * can't find any that are clearly freely redistributable.
28 #include "screenhack.h"
29 #include "xpm-pixmap.h"
35 #include <X11/Xutil.h>
36 #include <X11/Intrinsic.h>
38 #include "images/logo-50.xpm"
41 /* #define USE_TEST_PATTERNS */
43 #define countof(x) (sizeof((x))/sizeof((*x)))
45 static analogtv *tv=NULL;
47 analogtv_font ugly_font;
50 update_smpte_colorbars(analogtv_input *input)
57 SMPTE is the society of motion picture and television engineers, and
58 these are the standard color bars in the US. Following the partial spec
59 at http://broadcastengineering.com/ar/broadcasting_inside_color_bars/
60 These are luma, chroma, and phase numbers for each of the 7 bars.
62 double top_cb_table[7][3]={
63 {75, 0, 0.0}, /* gray */
64 {69, 31, 167.0}, /* yellow */
65 {56, 44, 283.5}, /* cyan */
66 {48, 41, 240.5}, /* green */
67 {36, 41, 60.5}, /* magenta */
68 {28, 44, 103.5}, /* red */
69 {15, 31, 347.0} /* blue */
71 double mid_cb_table[7][3]={
72 {15, 31, 347.0}, /* blue */
73 {7, 0, 0}, /* black */
74 {36, 41, 60.5}, /* magenta */
75 {7, 0, 0}, /* black */
76 {56, 44, 283.5}, /* cyan */
77 {7, 0, 0}, /* black */
78 {75, 0, 0.0} /* gray */
81 analogtv_lcp_to_ntsc(0.0, 0.0, 0.0, black_ntsc);
83 analogtv_setup_sync(input, 1, 0);
84 analogtv_setup_teletext(input);
86 for (col=0; col<7; col++) {
87 analogtv_draw_solid_rel_lcp(input, col*(1.0/7.0), (col+1)*(1.0/7.0), 0.00, 0.68,
89 top_cb_table[col][1], top_cb_table[col][2]);
91 analogtv_draw_solid_rel_lcp(input, col*(1.0/7.0), (col+1)*(1.0/7.0), 0.68, 0.75,
93 mid_cb_table[col][1], mid_cb_table[col][2]);
96 analogtv_draw_solid_rel_lcp(input, 0.0, 1.0/6.0,
97 0.75, 1.00, 7, 40, 303); /* -I */
98 analogtv_draw_solid_rel_lcp(input, 1.0/6.0, 2.0/6.0,
99 0.75, 1.00, 100, 0, 0); /* white */
100 analogtv_draw_solid_rel_lcp(input, 2.0/6.0, 3.0/6.0,
101 0.75, 1.00, 7, 40, 33); /* +Q */
102 analogtv_draw_solid_rel_lcp(input, 3.0/6.0, 4.0/6.0,
103 0.75, 1.00, 7, 0, 0); /* black */
104 analogtv_draw_solid_rel_lcp(input, 12.0/18.0, 13.0/18.0,
105 0.75, 1.00, 3, 0, 0); /* black -4 */
106 analogtv_draw_solid_rel_lcp(input, 13.0/18.0, 14.0/18.0,
107 0.75, 1.00, 7, 0, 0); /* black */
108 analogtv_draw_solid_rel_lcp(input, 14.0/18.0, 15.0/18.0,
109 0.75, 1.00, 11, 0, 0); /* black +4 */
110 analogtv_draw_solid_rel_lcp(input, 5.0/6.0, 6.0/6.0,
111 0.75, 1.00, 7, 0, 0); /* black */
115 xpos=ANALOGTV_VIS_START + ANALOGTV_VIS_LEN/2;
119 if (gethostname (localname, sizeof (localname))==0) {
120 localname[sizeof(localname)-1]=0; /* "The returned name is null-
121 terminated unless insufficient
122 space is provided" */
123 localname[24]=0; /* limit length */
125 analogtv_draw_string_centered(input, &ugly_font, localname,
126 xpos, ypos, black_ntsc);
129 ypos += ugly_font.char_h*5/2;
131 analogtv_draw_xpm(tv, input,
132 logo_50_xpm, xpos - 100, ypos);
137 analogtv_draw_string_centered(input, &ugly_font, "Please Stand By", xpos, ypos);
138 ypos += ugly_font.char_h*4;
143 time_t t = time ((time_t *) 0);
144 struct tm *tm = localtime (&t);
146 /* Y2K: It is OK for this to use a 2-digit year because it's simulating a
147 TV display and is purely decorative. */
148 strftime(timestamp, sizeof(timestamp)-1, "%y.%m.%d %H:%M:%S ", tm);
149 analogtv_draw_string_centered(input, &ugly_font, timestamp,
150 xpos, ypos, black_ntsc);
154 input->next_update_time += 1.0;
159 draw_color_square(analogtv_input *input)
163 analogtv_draw_solid_rel_lcp(input, 0.0, 1.0, 0.0, 1.0,
166 for (xs=0.0; xs<0.9999; xs+=1.0/15.0) {
167 analogtv_draw_solid_rel_lcp(input, xs, xs, 0.0, 1.0,
171 for (ys=0.0; ys<0.9999; ys+=1.0/11.0) {
172 analogtv_draw_solid_rel_lcp(input, 0.0, 1.0, ys, ys,
176 for (ys=0.0; ys<0.9999; ys+=0.01) {
178 analogtv_draw_solid_rel_lcp(input, 0.0/15, 1.0/15, ys, ys+0.01,
179 40.0, 45.0, 103.5*(1.0-ys) + 347.0*ys);
181 analogtv_draw_solid_rel_lcp(input, 14.0/15, 15.0/15, ys, ys+0.01,
182 40.0, 45.0, 103.5*(ys) + 347.0*(1.0-ys));
185 for (ys=0.0; ys<0.9999; ys+=0.02) {
186 analogtv_draw_solid_rel_lcp(input, 1.0/15, 2.0/15, ys*2.0/11.0+1.0/11.0,
187 (ys+0.01)*2.0/11.0+1.0/11.0,
188 100.0*(1.0-ys), 0.0, 0.0);
195 char *progclass = "XAnalogTV";
197 char *defaults [] = {
198 ".background: black",
199 ".foreground: white",
205 XrmOptionDescRec options [] = {
206 { "-delay", ".delay", XrmoptionSepArg, 0 },
212 #ifdef USE_TEST_PATTERNS
214 #include "images/earth.xpm"
216 char **test_patterns[] = {
224 N_CHANNELS=12, /* Channels 2 through 13 on VHF */
228 typedef struct chansetting_s {
230 analogtv_reception recs[MAX_MULTICHAN];
236 static struct timeval basetime;
242 gettimeofday(&tv,NULL);
243 return ((tv.tv_sec - basetime.tv_sec)*1000 +
244 (tv.tv_usec - basetime.tv_usec)/1000);
248 /* The first time we grab an image, do it the default way.
249 The second and subsequent times, add "-no-desktop" to the command.
250 That way we don't have to watch the window un-map 5+ times in a row.
251 Also, we end up with the desktop on only one channel, and pictures
252 on all the others (or colorbars, if no imageDirectory is set.)
255 hack_resources (void)
257 static int count = -1;
264 char *res = "desktopGrabber";
265 char *val = get_string_resource (res, "DesktopGrabber");
269 sprintf (buf1, "%.100s.%.100s", progclass, res);
270 sprintf (buf2, "%.200s -no-desktop", val);
272 value.size = strlen(buf2);
273 XrmPutResource (&db, buf1, "String", &value);
279 analogtv_load_random_image(analogtv *it, analogtv_input *input)
283 int width=ANALOGTV_PIC_LEN;
284 int height=width*3/4;
287 pixmap=XCreatePixmap(it->dpy, it->window, width, height, it->visdepth);
288 XSync(it->dpy, False);
290 load_random_image(it->screen, it->window, pixmap, NULL, NULL);
291 image = XGetImage(it->dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap);
292 XFreePixmap(it->dpy, pixmap);
294 /* Make sure the window's background is not set to None, and get the
295 grabbed bits (if any) off it as soon as possible. */
296 XSetWindowBackground (it->dpy, it->window,
297 get_pixel_resource ("background", "Background",
298 it->dpy, it->xgwa.colormap));
299 XClearWindow (it->dpy, it->window);
301 analogtv_setup_sync(input, 1, (random()%20)==0);
302 rc=analogtv_load_ximage(it, input, image);
303 if (image) XDestroyImage(image);
304 XSync(it->dpy, False);
309 analogtv_load_xpm(analogtv *it, analogtv_input *input, char **xpm)
316 pixmap=xpm_data_to_pixmap (it->dpy, it->window, xpm,
317 &width, &height, NULL);
318 image = XGetImage(it->dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap);
319 XFreePixmap(it->dpy, pixmap);
320 rc=analogtv_load_ximage(it, input, image);
321 if (image) XDestroyImage(image);
322 XSync(it->dpy, False);
326 enum { MAX_STATIONS = 6 };
327 static int n_stations;
328 static analogtv_input *stations[MAX_STATIONS];
331 void add_stations(void)
333 while (n_stations < MAX_STATIONS) {
334 analogtv_input *input=analogtv_input_allocate();
335 stations[n_stations++]=input;
338 input->updater = update_smpte_colorbars;
339 input->do_teletext=1;
341 #ifdef USE_TEST_PATTERNS
342 else if (random()%5==0) {
343 j=random()%countof(test_patterns);
344 analogtv_setup_sync(input);
345 analogtv_load_xpm(tv, input, test_patterns[j]);
346 analogtv_setup_teletext(input);
350 analogtv_load_random_image(tv, input);
351 input->do_teletext=1;
358 screenhack (Display *dpy, Window window)
365 chansetting chansettings[N_CHANNELS];
368 int delay = get_integer_resource("delay", "Integer");
369 if (delay < 1) delay = 1;
371 analogtv_make_font(dpy, window, &ugly_font, 7, 10, "6x10");
373 tv=analogtv_allocate(dpy, window);
374 tv->event_handler = screenhack_handle_event;
378 analogtv_set_defaults(tv, "");
382 tv->tint_control += pow(frand(2.0)-1.0, 7) * 180.0;
385 tv->color_control += frand(0.3);
388 for (i=0; i<N_CHANNELS; i++) {
389 memset(&chansettings[i], 0, sizeof(chansetting));
391 chansettings[i].noise_level = 0.06;
392 chansettings[i].dur = 1000*delay;
395 chansettings[i].dur=600;
399 for (stati=0; stati<MAX_MULTICHAN; stati++) {
400 analogtv_reception *rec=&chansettings[i].recs[stati];
403 station=random()%n_stations;
404 if (station!=last_station) break;
405 if ((random()%10)==0) break;
407 last_station=station;
408 rec->input = stations[station];
409 rec->level = pow(frand(1.0), 3.0) * 2.0 + 0.05;
410 rec->ofs=random()%ANALOGTV_SIGNAL_LEN;
412 rec->multipath = frand(1.0);
417 /* We only set a frequency error for ghosting stations,
418 because it doesn't matter otherwise */
419 rec->freqerr = (frand(2.0)-1.0) * 3.0;
422 if (rec->level > 0.3) break;
423 if (random()%4) break;
428 gettimeofday(&basetime,NULL);
431 cs=&chansettings[curinputi];
432 change_ticks = cs->dur + 1500;
436 int curticks=getticks();
437 double curtime=curticks*0.001;
440 if (analogtv_handle_events(tv)) {
444 if (change_now || (!using_mouse && curticks>=change_ticks
445 && tv->powerup > 10.0)) {
446 curinputi=(curinputi+1)%N_CHANNELS;
447 cs=&chansettings[curinputi];
448 change_ticks = curticks + cs->dur;
449 /* Set channel change noise flag */
450 tv->channel_change_cycles=200000;
453 for (i=0; i<MAX_MULTICHAN; i++) {
454 analogtv_reception *rec=&cs->recs[i];
455 analogtv_input *inp=rec->input;
459 inp->next_update_time = curtime;
462 rec->ofs += rec->freqerr;
467 analogtv_init_signal(tv, cs->noise_level);
468 for (i=0; i<MAX_MULTICHAN; i++) {
469 analogtv_reception *rec=&cs->recs[i];
470 analogtv_input *inp=rec->input;
473 analogtv_reception_update(rec);
474 analogtv_add_signal(tv, rec);
480 XClearWindow(dpy, window);
482 if (tv) analogtv_release(tv);