#ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include "clp.h" #include "actuator.hh" #include "alarm.hh" #include "bezier.hh" #include "expr.hh" #include "grafix.hh" #include "light.hh" #include "logic.hh" #include "sensor.hh" #include "shelf.hh" #include "token.hh" #include "vehicle.hh" #include "view.hh" #include "world.hh" #include "yuck.hh" class WorldMoveAlarm: public AlarmHooks { World *_world; View *_view; Alarm _alarm; int _time; bool _paused; Moment _interval; Moment _draw_interval; int _move_quantum; int _draw_quantum; void set_quanta() { _draw_quantum = (int)(_draw_interval / _interval); if (_draw_quantum <= 0) _draw_quantum = 1; _move_quantum = (int)(_interval / Moment(0, 2500)); if (_move_quantum <= 0) _move_quantum = 1; } public: WorldMoveAlarm(World *w, View *v) : _world(w), _view(v), _alarm(this), _time(0), _paused(0), _interval(0, 10000), _draw_interval(0, 30000) { set_quanta(); } bool paused() const { return _paused; } const Moment &interval() const { return _interval; } void set_interval(const Moment &m) { _interval = m; set_quanta(); } void set_draw_interval(const Moment &m) { _draw_interval = m; set_quanta(); } void begin() { _alarm.schedule(Moment::now()); } void set_paused(bool b) { _paused = b; } void alarm(Alarm *, const Moment &now) { if (!_paused) { for (int i = 0; i < _move_quantum; i++) { _world->move(); _world->check_collisions(); } if ((_time % _draw_quantum) == 0) _world->draw(_view); if ((_time % 6) == 0) _world->trail_snapshot(); _time++; } _alarm.schedule(now + _interval); } }; static Atom wm_protocols_atom; static Atom wm_delete_window_atom; static World *world; static View *view; static WorldMoveAlarm *wma; static bool quitting = false; static String::Initializer initializer; static String output_eps_name; static void output_trails(World *world, View *view) { FILE *f; static int ntimes = 0; if (!output_eps_name || output_eps_name == "-") f = stdout; else { ntimes++; char buf[128]; sprintf(buf, output_eps_name.c_str(), ntimes); f = fopen(buf, "w"); if (!f) { fprintf(stderr, "%s: %s\n", buf, strerror(errno)); return; } else fprintf(stderr, "EPS written to `%s'\n", buf); } int dash_pos = 0; EPSView epsview(view, f); fprintf(f, "%%!PS-Adobe-3.0 EPSF-3.0\n\ %%%%BoundingBox: 0 0 %d %d\n\ %%%%EndComments\n\ %%%%Page: 1 1\n", view->window_width(), view->window_height()); fprintf(f, "gsave 0 0 %d %d rectclip\n", view->window_width(), view->window_height()); double error = view->x_scale(); for (int i = 0; i < world->nbodies(); i++) { Vehicle *v = world->body(i)->cast_vehicle(); if (!v) { fprintf(f, "%% Body %d\n[] 0 setdash 0.7 setgray\n", i); world->body(i)->draw(&epsview); fprintf(f, "0 setgray\n"); continue; } fprintf(f, "%% Vehicle %d\n", i); switch (dash_pos) { case 0: fprintf(f, "[] 0 setdash\n"); break; case 1: fprintf(f, "[1 2] 0 setdash\n"); break; case 2: fprintf(f, "[5 3] 0 setdash\n"); break; case 3: fprintf(f, "[8 3 2 3] 0 setdash\n"); break; } dash_pos = (dash_pos + 1) % 4; const Vector &trail = v->trail(); Vector beziers; Bezier::fit(trail, error, beziers); if (beziers.size()) { Point cp = view->transform(beziers[0].point(0)); fprintf(f, "%g %g moveto\n", cp.x, cp.y); for (int j = 0; j < beziers.size(); j++) { const Bezier &b = beziers[j]; Point p1 = view->transform(b.point(1)) - cp; Point p2 = view->transform(b.point(2)) - cp; Point p3 = view->transform(b.point(3)) - cp; fprintf(f, "%g %g %g %g %g %g rcurveto\n", p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); cp += p3; } fprintf(f, "stroke\n"); } v->draw(&epsview); } fprintf(f, "grestore showpage\n%%%%EOF\n"); if (f != stdout) fclose(f); } bool handle_event(XEvent &e) { if (e.type != MappingNotify && e.xany.window != view->window()) return true; switch (e.type) { case Expose: if (e.xexpose.count == 0) world->draw(view); break; case KeyPress: { KeySym keysym; char buffer[5]; XComposeStatus compose; int nbuffer = XLookupString(&e.xkey, buffer, 5, &keysym, &compose); double shift_amount = (e.xkey.state & ControlMask ? 0.1111 : 0.3333); if (keysym == XK_Up) { view->screen_shift(0, shift_amount); world->draw(view); } else if (keysym == XK_Down) { view->screen_shift(0, -shift_amount); world->draw(view); } else if (keysym == XK_Left) { view->screen_shift(-shift_amount, 0); world->draw(view); } else if (keysym == XK_Right) { view->screen_shift(shift_amount, 0); world->draw(view); } else if (keysym == XK_Escape) { wma->set_paused(!wma->paused()); } else if (nbuffer == 1) { if (buffer[0] == 'r') { world->reset(); world->random_place(true, false, view); world->draw(view); } else if (buffer[0] == 'q') quitting = true; else if (buffer[0] == '-') { view->zoom(5./6); world->draw(view); } else if (buffer[0] == '=') { view->zoom(6./5); world->draw(view); } else if (buffer[0] == 't') { view->set_trails(!view->trails()); world->draw(view); } else if (buffer[0] == 'o') { output_trails(world, view); } else if (buffer[0] == ',' || buffer[0] == '<') { Moment interval = wma->interval(); interval *= 1.25; wma->set_interval(interval); } else if (buffer[0] == '.' || buffer[0] == '>') { Moment interval = wma->interval(); interval *= 0.8; wma->set_interval(interval); } } break; } /*case EnterNotify: draw_smile(true); break; case LeaveNotify: draw_smile(false); break; case ButtonPress: draw_tongue(true); if (e.xbutton.button == 2) do_drag(); break;*/ /*case MotionNotify: follow_right_eye(e.xmotion); follow_left_eye(e.xmotion); break;*/ /*case ButtonRelease: if (in_dsch_rect(e.xbutton.x, e.xbutton.y, LeftEyeOffsetX, LeftEyeOffsetY, LeftEyeWidth, LeftEyeHeight)) { // Check for knocking out the left eye... draw_left_eye(!_left_eye_dead); draw_smile(!_left_eye_dead); } else if (in_dsch_rect(e.xbutton.x, e.xbutton.y, RightEyeOffsetX, RightEyeOffsetY, RightEyeWidth, RightEyeHeight)) { // Check for knocking out the right eye... draw_right_eye(!_right_eye_dead); draw_smile(!_right_eye_dead); } else draw_tongue(false); if (_left_eye_dead && _right_eye_dead) { // If both eyes are now dead, let's quit! draw_tongue(true); die(); return false; } break;*/ /*case UnmapNotify: // We are unmapped mostly when the user tries to iconify us. // We are contrary and don't like to be iconified. Ha! XMapRaised(_display, _window); _just_iconified = 2; break;*/ case ConfigureNotify: view->set_window_size(e.xconfigure.width, e.xconfigure.height); world->draw(view); break; /* case ClientMessage: if (e.xclient.message_type == wm_protocols_atom && (Atom)(e.xclient.data.l[0]) == wm_delete_window_atom) draw_string(30, 80, "You can't delete me!"); break;*/ case MappingNotify: XRefreshKeyboardMapping(&e.xmapping); break; } return true; } /***** * Helper functions and main program **/ static const char *geometry_spec = 0; static const char *program_name; #if 0 static bool unparse_bool(const char *in_str) { if (strlen(in_str) > 5) return false; char buf[6], *s; for (s = buf; *in_str; in_str++, s++) *s = tolower(*in_str); *s = 0; return (strcmp(buf, "true") == 0 || strcmp(buf, "1") == 0 || strcmp(buf, "yes") == 0 || strcmp(buf, "y") == 0); } static void handle_resources(XrmDatabase xrmdb) { char *str_type; XrmValue value; if (on_root == -1) { if (XrmGetResource(xrmdb, "xshostakovich.root", "XShostakovich.Root", &str_type, &value)) on_root = (unparse_bool(value.addr) ? 1 : 0); else on_root = 0; } if (!geometry_spec) { if (XrmGetResource(xrmdb, "xshostakovich.geometry", "XShostakovich.Geometry", &str_type, &value)) geometry_spec = value.addr; } if (infinity == -1) { if (XrmGetResource(xrmdb, "xshostakovich.infinity", "XShostakovich.Infinity", &str_type, &value)) infinity = (unparse_bool(value.addr) ? 1 : 0); else infinity = 0; } if (!Shostakovich::saying) { if (XrmGetResource(xrmdb, "xshostakovich.saying", "XShostakovich.Saying", &str_type, &value)) Shostakovich::saying = value.addr; else Shostakovich::saying = "Hello, Dmitri"; } if (!set_quiet) if (XrmGetResource(xrmdb, "xshostakovich.quiet", "XShostakovich.Quiet", &str_type, &value)) Shostakovich::quiet = unparse_bool(value.addr); if (!Shostakovich::saying_color_name) { if (XrmGetResource(xrmdb, "xshostakovich.textColor", "XShostakovich.TextColor", &str_type, &value) == True) Shostakovich::saying_color_name = value.addr; else Shostakovich::saying_color_name = "MediumSeaGreen"; } if (!Shostakovich::font_name) if (XrmGetResource(xrmdb, "xshostakovich.font", "XShostakovich.Font", &str_type, &value)) Shostakovich::font_name = value.addr; } #endif static int parse_geometry(const char *const_g, XSizeHints *sh, int screen_width, int screen_height) { char *g = (char *)const_g; sh->flags = 0; if (isdigit(*g)) { sh->flags |= USSize; sh->width = strtol(g, &g, 10); if (g[0] == 'x' && isdigit(g[1])) sh->height = strtol(g + 1, &g, 10); else goto error; } else if (!*g) goto error; if (*g == '+' || *g == '-') { int x_minus, y_minus; sh->flags |= USPosition | PWinGravity; x_minus = *g == '-'; sh->x = strtol(g + 1, &g, 10); if (x_minus) sh->x = screen_width - sh->x - sh->width; y_minus = *g == '-'; if (*g == '-' || *g == '+') sh->y = strtol(g + 1, &g, 10); else goto error; if (y_minus) sh->y = screen_height - sh->y - sh->height; if (x_minus) sh->win_gravity = y_minus ? SouthEastGravity : NorthEastGravity; else sh->win_gravity = y_minus ? SouthWestGravity : NorthWestGravity; } else if (*g) goto error; return 1; error: fprintf(stderr, "%s: bad geometry specification\n", program_name); sh->flags = 0; return 0; } static Window create_top_level(Display *display, int screen, Colormap colormap) { XClassHint classh; XTextProperty window_name, icon_name; XSetWindowAttributes setattr; unsigned long setattr_mask; // Set up window name and icon name { char *string_list[2]; string_list[0] = "xbraitenberg"; string_list[1] = 0; XStringListToTextProperty(string_list, 1, &window_name); XStringListToTextProperty(string_list, 1, &icon_name); classh.res_name = "xbraitenberg"; classh.res_class = "XBraitenberg"; } setattr.colormap = colormap = DefaultColormap(display, screen); setattr.backing_store = NotUseful; setattr.save_under = False; setattr.border_pixel = 0; setattr.background_pixel = 0; setattr_mask = CWColormap | CWBorderPixel | CWBackPixel | CWBackingStore | CWSaveUnder; XSizeHints *xsizeh = XAllocSizeHints(); xsizeh->width = 640; xsizeh->height = 520; if (geometry_spec) parse_geometry(geometry_spec, xsizeh, DisplayWidth(display, screen), DisplayHeight(display, screen)); Window window = XCreateWindow (display, DefaultRootWindow(display), xsizeh->x, xsizeh->y, xsizeh->width, xsizeh->height, 0, DefaultDepth(display, screen), InputOutput, DefaultVisual(display, screen), setattr_mask, &setattr); XSetWMProperties(display, window, &window_name, &icon_name, NULL, 0, xsizeh, 0, &classh); XSetWMProtocols(display, window, &wm_delete_window_atom, 1); XSelectInput(display, window, ExposureMask | EnterWindowMask | LeaveWindowMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask); XMapRaised(display, window); XFree(xsizeh); XFree(window_name.value); XFree(icon_name.value); return window; } enum { XRM_OPT = 300, DISPLAY_OPT, GEOMETRY_OPT, VERSION_OPT, HELP_OPT, DOUBLE_BUFFER_OPT, TRAILS_OPT, OUTPUT_EPS_OPT, LEFT_LOGIC_OPT, RIGHT_LOGIC_OPT, CROSS_LOGIC_OPT, STATIC_FRICTION_OPT, DYNAMIC_FRICTION_OPT, FRICTION_OPT, ANG_FRICTION_OPT, INHIBIT_OPT, EXCITE_OPT, LIGHTS_OPT }; static Clp_Option options[] = { { "af", 0, ANG_FRICTION_OPT, Clp_ArgDouble, 0 }, { "angular-friction", 0, ANG_FRICTION_OPT, Clp_ArgDouble, Clp_Negate }, { "cross-logic", 'x', CROSS_LOGIC_OPT, 0, Clp_Negate }, { "db", 0, DOUBLE_BUFFER_OPT, 0, Clp_Negate }, { "df", 0, DYNAMIC_FRICTION_OPT, Clp_ArgDouble, 0 }, { "display", 'd', DISPLAY_OPT, Clp_ArgStringNotOption, 0 }, { "double-buffer", 0, DOUBLE_BUFFER_OPT, 0, Clp_Negate }, { "dynamic-friction", 0, DYNAMIC_FRICTION_OPT, Clp_ArgDouble, 0 }, { "friction", 'f', FRICTION_OPT, Clp_ArgDouble, 0 }, { "geometry", 0, GEOMETRY_OPT, Clp_ArgString, 0 }, { "lights", 'l', LIGHTS_OPT, Clp_ArgDouble, Clp_Optional | Clp_Negate }, { "left-logic", 0, LEFT_LOGIC_OPT, Clp_ArgString, 0 }, { "ll", 0, LEFT_LOGIC_OPT, Clp_ArgString, 0 }, { "help", 'h', HELP_OPT, 0, 0 }, { "output-eps", 'o', OUTPUT_EPS_OPT, Clp_ArgString, 0 }, { "right-logic", 0, RIGHT_LOGIC_OPT, Clp_ArgString, 0 }, { "rl", 0, RIGHT_LOGIC_OPT, Clp_ArgString, 0 }, { "sf", 0, STATIC_FRICTION_OPT, Clp_ArgDouble, 0 }, { "static-friction", 0, STATIC_FRICTION_OPT, Clp_ArgDouble, 0 }, { "trails", 't', TRAILS_OPT, 0, Clp_Negate }, { "version", 'v', VERSION_OPT, 0, 0 }, { "xrm", 0, XRM_OPT, Clp_ArgString, 0 }, }; static void short_usage() { fprintf(stderr, "Usage: %s [OPTION]...\n\ Try `%s --help' for more information.\n", program_name, program_name); } static void usage() { printf("\ `Xbraitenberg' is a program to simulate one or more Braitenberg vehicles.\n\ \n\ Usage: %s [OPTION]...\n\ \n\ Options are:\n\ -display DISPLAY Set the X display for the Xbraitenberg window\n\ -geometry GEOMETRY Set the Xbraitenberg window's geometry\n\ -db, -double-buffer Use double-buffering (better display, slower)\n\ -t, -trails Show vehicle trails\n\ -o, -output-eps FILE Name output EPS files `FILE'\n\ -h, --help Print this message and exit\n\ -v, --version Print version number and exit\n\ \n\ -f, -friction K Set world's coefficient of friction to K\n\ -sf, -static-friction K Set world's static friction coefficient to K\n\ -df, -dynamic-friction K Set world's dynamic friction coefficient to K\n\ -af, -angular-friction K Set \"coefficient of angular friction\" to K\n\ \n\ -l[VAL], -lights[=VAL] Add lights to vehicles, strength VAL (default 6)\n\ -x, -cross-logic Switch logic for left and right actuators\n\ -ll, -left-logic EXP Set logic for wiring to left actuator\n\ -rl, -right-logic EXP Set logic for wiring to right actuator\n\ \n\ Keystrokes:\n\ up, down, left, right Pan through space (Control- moves less)\n\ =, - Zoom in and out\n\ <, > Animate slower or faster\n\ Escape Pause/resume\n\ t Toggle trails display\n\ o Output current trails in EPS format\n\ r Restart with all vehicles in random positions\n\ q Quit\n\ \n\ Reporting bugs? \n", program_name); } static void add_vehicles_2_3(Vehicle *v, PermString suffix, Shelf *shelf) { static Logic *l_logic = 0; static Logic *r_logic, *pin, *f_l_logic, *f_r_logic; if (!l_logic) { l_logic = new LinearLogic(0, 0, 1); r_logic = new LinearLogic(1, 0, 1); pin = new ConstExpr(0.3); f_l_logic = new BinaryExpr(new UnaryExpr(opFlip, l_logic), '*', pin); f_r_logic = new BinaryExpr(new UnaryExpr(opFlip, r_logic), '*', pin); } v = v->clone(); v->set_logic(0, l_logic); v->set_logic(1, r_logic); shelf->add(permcat("v2a", suffix), v); v = v->clone(); v->set_logic(0, r_logic); v->set_logic(1, l_logic); v->set_color(0, 0, 255); shelf->add(permcat("v2b", suffix), v); v = v->clone(); v->set_logic(0, f_l_logic); v->set_logic(1, f_r_logic); v->set_color(0, 255, 0); shelf->add(permcat("v3a", suffix), v); v = v->clone(); v->set_logic(0, f_r_logic); v->set_logic(1, f_l_logic); v->set_color(255, 128, 0); shelf->add(permcat("v3b", suffix), v); } static void make_default_vehicles(Shelf *shelf) { Actuator *m_actuator = new Actuator(Point(-10, 0), 1); Vehicle *v = new Vehicle(20, 12); v->add_sensor(new Sensor(Point(10, 0), -M_PI, M_PI, 1, "middle")); v->add_actuator(m_actuator, new LinearLogic(0, 0, 1)); shelf->add("v1", v); v = new Vehicle(20, 12); v->add_sensor(new Sensor(Point(10, 0), -M_PI/2, M_PI/2, 1, "middle")); v->add_actuator(m_actuator, new LinearLogic(0, 0, 1)); shelf->add("v1p", v); Actuator *l_actuator = new Actuator(Point(-10, -10), 1); Actuator *r_actuator = new Actuator(Point(-10, 10), 1); v = new Vehicle(20, 12); v->add_sensor(new Sensor(Point(10, -6), -M_PI, M_PI, 1, "left")); v->add_sensor(new Sensor(Point(10, +6), -M_PI, M_PI, 1, "right")); v->add_actuator(l_actuator); v->add_actuator(r_actuator); add_vehicles_2_3(v, "", shelf); v = new Vehicle(20, 12); v->add_sensor(new Sensor(Point(10, -6), -3*M_PI/4, M_PI/2, 1, "left")); v->add_sensor(new Sensor(Point(10, +6), -M_PI/2, 3*M_PI/4, 1, "right")); v->add_actuator(l_actuator); v->add_actuator(r_actuator); add_vehicles_2_3(v, "p", shelf); shelf->add("l", new Light(Point(0, 0), -M_PI, M_PI, 8, 1)); shelf->add("timel", new TimedLight(Point(0, 0), -M_PI, M_PI, 8, 1, 320)); shelf->add("l2a", new Light(Point(-250, 0), -M_PI, M_PI, 10, 1)); shelf->add("l2b", new Light(Point(250, 0), -M_PI, M_PI, 10, 1)); } int main(int argc, char *argv[]) { const char *display_name = 0; const char **saved_resources = new const char *[argc]; int nsaved_resources = 0; // handle command line Clp_Parser *clp = Clp_NewParser(argc, (char * const *)argv, sizeof(options) / sizeof(Clp_Option), options); Clp_SetOptionChar(clp, '-', Clp_Long | Clp_Short); Clp_SetOptionChar(clp, '+', Clp_LongNegated | Clp_ShortNegated); program_name = Clp_ProgramName(clp); Shelf *shelf = new Shelf; make_default_vehicles(shelf); bool cross_logic = 0; Logic *left_logic = 0; Logic *right_logic = 0; bool double_buffering = 0; bool trails = 0; output_eps_name = "xbraitenberg.eps.%03d"; bool lights = 0; //Light *headlight = new ThresholdLight(0, -M_PI, M_PI, 6, 1, .08); //Light *headlight = new Light((Body *)0, -2*M_PI/5, 2*M_PI/5, 6, 1); Light *headlight = new Light((Body *)0, -M_PI, M_PI, 6, 1); world = new World; while (1) { int opt = Clp_Next(clp); switch (opt) { case XRM_OPT: saved_resources[nsaved_resources] = clp->arg; nsaved_resources++; break; case DISPLAY_OPT: display_name = clp->arg; break; case GEOMETRY_OPT: geometry_spec = clp->arg; break; case DOUBLE_BUFFER_OPT: double_buffering = (clp->negated ? false : true); break; case TRAILS_OPT: trails = (clp->negated ? false : true); break; case OUTPUT_EPS_OPT: { bool percent = false, bad = false; for (const char *c = output_eps_name.c_str(); !bad && *c; c++) if (*c == '%') { c++; while ((*c >= '0' && *c <= '9') || *c == '-' || *c == '#' || *c == ' ' || *c == '+') c++; if (*c == '%') /* OK */; else if (*c != 'u' && *c != 'd' && *c != 'i') bad = true; else if (percent) bad = true; else percent = true; } if (bad) fprintf(stderr, "%s: bad output EPS name\n", program_name); else output_eps_name = clp->arg; break; } case LEFT_LOGIC_OPT: { StringTokenBufferFiller stbf("", clp->arg); Tokenizer t(&stbf); Yuck yuck(&t); left_logic = yuck.yexpr(); break; } case RIGHT_LOGIC_OPT: { StringTokenBufferFiller stbf("", clp->arg); Tokenizer t(&stbf); Yuck yuck(&t); right_logic = yuck.yexpr(); break; } case CROSS_LOGIC_OPT: cross_logic = (clp->negated ? false : true); break; case LIGHTS_OPT: lights = (clp->negated ? false : true); if (lights && clp->have_arg) headlight->set_strength(clp->val.d); break; case FRICTION_OPT: world->set_static_friction(clp->val.d); world->set_dynamic_friction(0.8 * clp->val.d); break; case STATIC_FRICTION_OPT: world->set_static_friction(clp->val.d); break; case DYNAMIC_FRICTION_OPT: world->set_dynamic_friction(clp->val.d); break; case ANG_FRICTION_OPT: world->set_angular_friction(clp->val.d); break; case VERSION_OPT: printf("LCDF Xbraitenberg version %s\n", VERSION); printf("Copyright (C) 1999-2006 Eddie Kohler\n\ This is free software; see the source for copying conditions.\n\ There is NO warranty, not even for merchantability or fitness for a\n\ particular purpose.\n"); exit(0); break; case HELP_OPT: usage(); exit(0); break; case Clp_Done: goto done; case Clp_NotOption: { PermString name = clp->arg; Body *body = shelf->find(name); if (!body) fprintf(stderr, "%s: no such body `%s'\n", program_name, clp->arg); else if (Vehicle *v = body->cast_vehicle()) { v = v->clone(); if (lights) v->make_attachment(headlight); if (left_logic) v->set_logic(0, left_logic); if (right_logic) v->set_logic(1, right_logic); if (cross_logic) { Logic *l0 = v->logic(0); Logic *l1 = v->logic(1); v->set_logic(0, l1); v->set_logic(1, l0); } world->add(v); } else world->add(body); break; } case Clp_BadOption: short_usage(); exit(1); break; } } done: Display *display = XOpenDisplay(display_name); int screen = DefaultScreen(display); Colormap colormap = DefaultColormap(display, screen); wm_protocols_atom = XInternAtom(display, "WM_PROTOCOLS", False); wm_delete_window_atom = XInternAtom(display, "WM_DELETE_WINDOW", False); XrmInitialize(); XrmDatabase xrmdb = 0; if (XResourceManagerString(display)) xrmdb = XrmGetStringDatabase(XResourceManagerString(display)); if (!xrmdb) xrmdb = XrmGetStringDatabase(""); for (int i = 0; i < nsaved_resources; i++) XrmPutLineResource(&xrmdb, saved_resources[i]); //handle_resources(xrmdb); Window window = create_top_level(display, screen, colormap); Grafix grafix(display, window); view = new View(&grafix, window); view->set_double_buffer(double_buffering); view->set_trails(trails); wma = new WorldMoveAlarm(world, view); wma->begin(); wma->set_draw_interval(Moment(0, double_buffering ? 120000 : 30000)); world->random_place(true, false, view); XEvent event; while (!quitting) { while (XPending(display)) { XNextEvent(display, &event); handle_event(event); } Alarm::x_wait(display); } XDestroyWindow(display, window); return 0; }