コード例 #1
0
/*
 * timer - event timer
 */
void
timer(void)
{
	register struct peer *peer, *next_peer;
	u_int	n;
	long delta;

	/*
	 * The basic timerevent is one second. This is used to adjust
	 * the system clock in time and frequency, implement the
	 * kiss-o'-deatch function and implement the association
	 * polling function..
	 */
	current_time += nap_time;
	get_systime(&sys_time);
	nap_time = (u_long)-1;

	if (do_adjtime) {
		if (adjust_timer <= current_time) {
			adjust_timer += 1;
			adj_host_clock();
#ifdef REFCLOCK
			for (n = 0; n < NTP_HASH_SIZE; n++) {
				for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
					next_peer = peer->next;
					if (peer->flags & FLAG_REFCLOCK)
						refclock_timer(peer);
				}
			}
#endif /* REFCLOCK */
		}
        nap_time = 1;
    }
    
    if (awake_timer) {
        if (awake_timer <= current_time) {
            for (n = 0; n < NTP_HASH_SIZE; n++) {
                for (peer = peer_hash[n]; peer != 0; peer = peer->next) {
                    peer->burst = NSTAGE;
                    peer->nextdate = current_time;
                    peer->throttle = 0;
                }
            }
            allow_panic = TRUE; /* Allow for large time offsets */
            init_loopfilter();
            state = EVNT_NSET;
            awake_timer = 0;
        } else {
            delta = awake_timer - current_time;
            if (delta < nap_time) {
                nap_time = delta;
            }
        }
    }
 
	/*
	 * Now dispatch any peers whose event timer has expired. Be
	 * careful here, since the peer structure might go away as the
	 * result of the call.
	 */
	for (n = 0; n < NTP_HASH_SIZE; n++) {
		for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
			next_peer = peer->next;
			if (peer->action && peer->nextaction <= current_time)
				peer->action(peer);

			/*
			 * Restrain the non-burst packet rate not more
			 * than one packet every 16 seconds. This is
			 * usually tripped using iburst and minpoll of
			 * 128 s or less.
			 */
			if (peer->throttle > 0)
				peer->throttle--;
			if (peer->nextdate <= current_time) {
#ifdef REFCLOCK
				if (peer->flags & FLAG_REFCLOCK)
					refclock_transmit(peer);
				else
					transmit(peer);
#else /* REFCLOCK */
				transmit(peer);
#endif /* REFCLOCK */
			}

			if (peer->action && peer->nextaction >= current_time) {
				delta = peer->nextaction - current_time;
				if (delta < nap_time) {
                    nap_time = delta;
                }
			} else if (peer->nextdate >= current_time) {
                delta = peer->nextdate - current_time;
                if (delta < nap_time) {
                    nap_time = delta;
                }
            }
		}
	}

	/*
	 * Orphan mode is active when enabled and when no servers less
	 * than the orphan statum are available. A server with no other
	 * synchronization source is an orphan It shows offset zero and
	 * reference ID the loopback address.
	 */
	if (sys_orphan < STRATUM_UNSPEC && sys_peer == NULL) {
		if (sys_leap == LEAP_NOTINSYNC) {
			sys_leap = LEAP_NOWARNING;
#ifdef OPENSSL
			if (crypto_flags)	
				crypto_update();
#endif /* OPENSSL */
		}
		sys_stratum = (u_char)sys_orphan;
		if (sys_stratum > 1)
			sys_refid = htonl(LOOPBACKADR);
		else
			memcpy(&sys_refid, "LOOP", 4);
		sys_offset = 0;
		sys_rootdelay = 0;
		sys_rootdisp = 0;
	}

	/*
	 * Leapseconds. If a leap is pending, decrement the time
	 * remaining. If less than one day remains, set the leap bits.
	 * When no time remains, clear the leap bits and increment the
	 * TAI. If kernel suppport is not available, do the leap
	 * crudely. Note a leap cannot be pending unless the clock is
	 * set.
	 */
	if (leapsec > 0) {
		leapsec--;
		if (leapsec == 0) {
			sys_leap = LEAP_NOWARNING;
			sys_tai = leap_tai;
#ifdef KERNEL_PLL
			if (!(pll_control && kern_enable))
				step_systime(-1.0);
#else /* KERNEL_PLL */
#ifndef SYS_WINNT /* WinNT port has its own leap second handling */
			step_systime(-1.0);
#endif /* SYS_WINNT */
#endif /* KERNEL_PLL */
			report_event(EVNT_LEAP, NULL, NULL);
		} else {
			if (leapsec < DAY)
				sys_leap = LEAP_ADDSECOND;
			if (leap_tai > 0)
				sys_tai = leap_tai - 1;
		}
	}

	/*
	 * Update huff-n'-puff filter.
	 */
	if (huffpuff_timer <= current_time) {
		huffpuff_timer += HUFFPUFF;
		huffpuff();
	}

	if (huffpuff_timer >= current_time) {
		delta = huffpuff_timer - current_time;
		if (delta < nap_time) {
            nap_time = delta;
        }
	}

#ifdef OPENSSL
	/*
	 * Garbage collect expired keys.
	 */
	if (keys_timer <= current_time) {
		keys_timer += 1 << sys_automax;
		auth_agekeys();
	}

	if (keys_timer >= current_time) {
		delta = keys_timer - current_time;
		if (delta < nap_time) {
            nap_time = delta;
        }
	}

	/*
	 * Garbage collect key list and generate new private value. The
	 * timer runs only after initial synchronization and fires about
	 * once per day.
	 */
	if (revoke_timer <= current_time && sys_leap !=
	    LEAP_NOTINSYNC) {
		revoke_timer += 1 << sys_revoke;
		RAND_bytes((u_char *)&sys_private, 4);
	}

	if (revoke_timer >= current_time) {
		delta = revoke_timer - current_time;
		if (delta < nap_time) {
            nap_time = delta;
        }
	}
#endif /* OPENSSL */

	/*
	 * Interface update timer
	 */
	if (interface_interval && interface_timer <= current_time) {

		timer_interfacetimeout(current_time +
		    interface_interval);
		DPRINTF(2, ("timer: interface update\n"));
		interface_update(NULL, NULL);
	}
    if (interface_interval && interface_timer >= current_time) {
        delta = interface_timer - current_time;
        if (delta < nap_time) {
            nap_time = delta;
        }
    }

	if (dns_timer && (dns_timer <= current_time)) {
		dns_timer = update_dns_peers();
	}

	if (dns_timer >= current_time) {
		delta = dns_timer - current_time;
		if (delta < nap_time) {
            nap_time = delta;
        }
	}

	/*
	 * Finally, write hourly stats.
	 */
	if (stats_timer <= current_time) {
		stats_timer += HOUR;
		write_stats();
		if (sys_tai != 0 && sys_time.l_ui > leap_expire)
			report_event(EVNT_LEAPVAL, NULL, NULL);
	} else if (!do_adjtime && drift_file_sw) {
        write_stats(); /* update more frequently for pacemaker */
    }
	
	if (stats_timer >= current_time) {
		delta = stats_timer - current_time;
		if (delta < nap_time) {
            nap_time = delta;
        }
	}

    if (nap_time == 0) {
        nap_time = 1;
    }
    if (debug) {
        msyslog(LOG_INFO, "%s: current_time: %ld, nap_time: %ld", __FUNCTION__,
                current_time, nap_time);
    }
	itimer.it_interval.tv_sec = itimer.it_value.tv_sec = nap_time;
	setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
}
コード例 #2
0
ファイル: ntp_timer.c プロジェクト: sambuc/netbsd
/*
 * timer - event timer
 */
void
timer(void)
{
	struct peer *	p;
	struct peer *	next_peer;
	l_fp		now;
	time_t          tnow;

	/*
	 * The basic timerevent is one second.  This is used to adjust the
	 * system clock in time and frequency, implement the kiss-o'-death
	 * function and the association polling function.
	 */
	current_time++;
	if (adjust_timer <= current_time) {
		adjust_timer += 1;
		adj_host_clock();
#ifdef REFCLOCK
		for (p = peer_list; p != NULL; p = next_peer) {
			next_peer = p->p_link;
			if (FLAG_REFCLOCK & p->flags)
				refclock_timer(p);
		}
#endif /* REFCLOCK */
	}

	/*
	 * Now dispatch any peers whose event timer has expired. Be
	 * careful here, since the peer structure might go away as the
	 * result of the call.
	 */
	for (p = peer_list; p != NULL; p = next_peer) {
		next_peer = p->p_link;

		/*
		 * Restrain the non-burst packet rate not more
		 * than one packet every 16 seconds. This is
		 * usually tripped using iburst and minpoll of
		 * 128 s or less.
		 */
		if (p->throttle > 0)
			p->throttle--;
		if (p->nextdate <= current_time) {
#ifdef REFCLOCK
			if (FLAG_REFCLOCK & p->flags)
				refclock_transmit(p);
			else
#endif	/* REFCLOCK */
				transmit(p);
		}
	}

	/*
	 * Orphan mode is active when enabled and when no servers less
	 * than the orphan stratum are available. A server with no other
	 * synchronization source is an orphan. It shows offset zero and
	 * reference ID the loopback address.
	 */
	if (sys_orphan < STRATUM_UNSPEC && sys_peer == NULL &&
	    current_time > orphwait) {
		if (sys_leap == LEAP_NOTINSYNC) {
			set_sys_leap(LEAP_NOWARNING);
#ifdef AUTOKEY
			if (crypto_flags)
				crypto_update();
#endif	/* AUTOKEY */
		}
		sys_stratum = (u_char)sys_orphan;
		if (sys_stratum > 1)
			sys_refid = htonl(LOOPBACKADR);
		else
			memcpy(&sys_refid, "LOOP", 4);
		sys_offset = 0;
		sys_rootdelay = 0;
		sys_rootdisp = 0;
	}

	get_systime(&now);
	time(&tnow);

	/*
	 * Leapseconds. Get time and defer to worker if either something
	 * is imminent or every 8th second.
	 */
	if (leapsec > LSPROX_NOWARN || 0 == (current_time & 7))
		check_leapsec(now.l_ui, &tnow,
                                (sys_leap == LEAP_NOTINSYNC));
        if (sys_leap != LEAP_NOTINSYNC) {
                if (leapsec >= LSPROX_ANNOUNCE && leapdif) {
		        if (leapdif > 0)
			        set_sys_leap(LEAP_ADDSECOND);
		        else
			        set_sys_leap(LEAP_DELSECOND);
                } else {
                        set_sys_leap(LEAP_NOWARNING);
                }
	}

	/*
	 * Update huff-n'-puff filter.
	 */
	if (huffpuff_timer <= current_time) {
		huffpuff_timer += HUFFPUFF;
		huffpuff();
	}

#ifdef AUTOKEY
	/*
	 * Garbage collect expired keys.
	 */
	if (keys_timer <= current_time) {
		keys_timer += 1 << sys_automax;
		auth_agekeys();
	}

	/*
	 * Generate new private value. This causes all associations
	 * to regenerate cookies.
	 */
	if (revoke_timer && revoke_timer <= current_time) {
		revoke_timer += 1 << sys_revoke;
		RAND_bytes((u_char *)&sys_private, 4);
	}
#endif	/* AUTOKEY */

	/*
	 * Interface update timer
	 */
	if (interface_interval && interface_timer <= current_time) {
		timer_interfacetimeout(current_time +
		    interface_interval);
		DPRINTF(2, ("timer: interface update\n"));
		interface_update(NULL, NULL);
	}

	if (worker_idle_timer && worker_idle_timer <= current_time)
		worker_idle_timer_fired();

	/*
	 * Finally, write hourly stats and do the hourly
	 * and daily leapfile checks.
	 */
	if (stats_timer <= current_time) {
		stats_timer += SECSPERHR;
		write_stats();
		if (leapf_timer <= current_time) {
			leapf_timer += SECSPERDAY;
			check_leap_file(TRUE, now.l_ui, &tnow);
		} else {
			check_leap_file(FALSE, now.l_ui, &tnow);
		}
	}
}
コード例 #3
0
ファイル: ntp_timer.c プロジェクト: 2014-class/freerouter
/*
 * timer - dispatch anyone who needs to be
 */
void
timer(void)
{
	register struct peer *peer, *next_peer;
#ifdef OPENSSL
	char	statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
#endif /* OPENSSL */
	u_int n;

	current_time += (1<<EVENT_TIMEOUT);

	/*
	 * Adjustment timeout first.
	 */
	if (adjust_timer <= current_time) {
		adjust_timer += 1;
		adj_host_clock();
		kod_proto();
#ifdef REFCLOCK
		for (n = 0; n < NTP_HASH_SIZE; n++) {
			for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
				next_peer = peer->next;
				if (peer->flags & FLAG_REFCLOCK)
					refclock_timer(peer);
			}
		}
#endif /* REFCLOCK */
	}

	/*
	 * Now dispatch any peers whose event timer has expired. Be careful
	 * here, since the peer structure might go away as the result of
	 * the call.
	 */
	for (n = 0; n < NTP_HASH_SIZE; n++) {
		for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
			next_peer = peer->next;
			if (peer->action && peer->nextaction <= current_time)
	  			peer->action(peer);
			if (peer->nextdate <= current_time) {
#ifdef REFCLOCK
				if (peer->flags & FLAG_REFCLOCK)
					refclock_transmit(peer);
				else
					transmit(peer);
#else /* REFCLOCK */
				transmit(peer);
#endif /* REFCLOCK */
			}
		}
	}

	/*
	 * Garbage collect expired keys.
	 */
	if (keys_timer <= current_time) {
		keys_timer += MINUTE;
		auth_agekeys();
	}

	/*
	 * Huff-n'-puff filter
	 */
	if (huffpuff_timer <= current_time) {
		huffpuff_timer += HUFFPUFF;
		huffpuff();
	}

#ifdef OPENSSL
	/*
	 * Garbage collect old keys and generate new private value
	 */
	if (revoke_timer <= current_time) {
		revoke_timer += RANDPOLL(sys_revoke);
		expire_all();
		sprintf(statstr, "refresh ts %u", ntohl(hostval.tstamp));
		record_crypto_stats(NULL, statstr);
#ifdef DEBUG
		if (debug)
			printf("timer: %s\n", statstr);
#endif
	}
#endif /* OPENSSL */

	/*
	 * interface update timer
	 */
	if (interface_interval && interface_timer <= current_time) {

		timer_interfacetimeout(current_time + interface_interval);
		DPRINTF(1, ("timer: interface update\n"));
		interface_update(NULL, NULL);
	}
	
	/*
	 * Finally, periodically write stats.
	 */
	if (stats_timer <= current_time) {
	     if (stats_timer != 0)
		  write_stats();
	     stats_timer += stats_write_period;
	}
}
コード例 #4
0
ファイル: freeserf.c プロジェクト: vaLinBSD/freeserf
/* game_loop() has been turned into a SDL based loop.
   The code for one iteration of the original game_loop is
   in game_loop_iter. */
static void
game_loop()
{
	/* FPS */
	int fps = 0;
	int fps_ema = 0;
	int fps_target = 25;
	const float ema_alpha = 0.003;

	int drag_button = 0;

	unsigned int last_down[3] = {0};
	unsigned int last_click[3] = {0};

	unsigned int current_ticks = SDL_GetTicks();
	unsigned int accum = 0;
	unsigned int accum_frames = 0;

	SDL_Event event;
	gui_event_t ev;

	game_loop_run = 1;
	while (game_loop_run) {
		if (SDL_PollEvent(&event)) {
			switch (event.type) {
			case SDL_MOUSEBUTTONUP:
				if (drag_button == event.button.button) {
					ev.type = GUI_EVENT_TYPE_DRAG_END;
					ev.x = event.button.x;
					ev.y = event.button.y;
					ev.button = drag_button;
					gui_object_handle_event((gui_object_t *)&interface, &ev);

					drag_button = 0;
				}

				ev.type = GUI_EVENT_TYPE_BUTTON_UP;
				ev.x = event.button.x;
				ev.y = event.button.y;
				ev.button = event.button.button;
				gui_object_handle_event((gui_object_t *)&interface, &ev);

				if (event.button.button <= 3 &&
				    current_ticks - last_down[event.button.button-1] < MOUSE_SENSITIVITY) {
					ev.type = GUI_EVENT_TYPE_CLICK;
					ev.x = event.button.x;
					ev.y = event.button.y;
					ev.button = event.button.button;
					gui_object_handle_event((gui_object_t *)&interface, &ev);

					if (current_ticks - last_click[event.button.button-1] < MOUSE_SENSITIVITY) {
						ev.type = GUI_EVENT_TYPE_DBL_CLICK;
						ev.x = event.button.x;
						ev.y = event.button.y;
						ev.button = event.button.button;
						gui_object_handle_event((gui_object_t *)&interface, &ev);
					}

					last_click[event.button.button-1] = current_ticks;
				}
				break;
			case SDL_MOUSEBUTTONDOWN:
				ev.type = GUI_EVENT_TYPE_BUTTON_DOWN;
				ev.x = event.button.x;
				ev.y = event.button.y;
				ev.button = event.button.button;
				gui_object_handle_event((gui_object_t *)&interface, &ev);

				if (event.button.button <= 3) last_down[event.button.button-1] = current_ticks;
				break;
			case SDL_MOUSEMOTION:
				if (drag_button == 0) {
					/* Move pointer normally. */
					interface_set_cursor(&interface, event.motion.x, event.motion.y);
				}

				for (int button = 1; button <= 3; button++) {
					if (event.motion.state & SDL_BUTTON(button)) {
						if (drag_button == 0) {
							drag_button = button;

							ev.type = GUI_EVENT_TYPE_DRAG_START;
							ev.x = event.motion.x;
							ev.y = event.motion.y;
							ev.button = drag_button;
							gui_object_handle_event((gui_object_t *)&interface, &ev);
						}

						ev.type = GUI_EVENT_TYPE_DRAG_MOVE;
						ev.x = event.motion.x;
						ev.y = event.motion.y;
						ev.button = drag_button;
						gui_object_handle_event((gui_object_t *)&interface, &ev);
						break;
					}
				}
				break;
			case SDL_KEYDOWN:
				if (event.key.keysym.sym == SDLK_q &&
				    (event.key.keysym.mod & KMOD_CTRL)) {
					game_loop_quit();
					break;
				}

				switch (event.key.keysym.sym) {
					/* Map scroll */
				case SDLK_UP: {
					viewport_t *viewport = interface_get_top_viewport(&interface);
					viewport_move_by_pixels(viewport, 0, -1);
				}
					break;
				case SDLK_DOWN: {
					viewport_t *viewport = interface_get_top_viewport(&interface);
					viewport_move_by_pixels(viewport, 0, 1);
				}
					break;
				case SDLK_LEFT: {
					viewport_t *viewport = interface_get_top_viewport(&interface);
					viewport_move_by_pixels(viewport, -1, 0);
				}
					break;
				case SDLK_RIGHT: {
					viewport_t *viewport = interface_get_top_viewport(&interface);
					viewport_move_by_pixels(viewport, 1, 0);
				}
					break;

					/* Panel click shortcuts */
				case SDLK_1: {
					panel_bar_t *panel = interface_get_panel_bar(&interface);
					panel_bar_activate_button(panel, 0);
				}
					break;
				case SDLK_2: {
					panel_bar_t *panel = interface_get_panel_bar(&interface);
					panel_bar_activate_button(panel, 1);
				}
					break;
				case SDLK_3: {
					panel_bar_t *panel = interface_get_panel_bar(&interface);
					panel_bar_activate_button(panel, 2);
				}
					break;
				case SDLK_4: {
					panel_bar_t *panel = interface_get_panel_bar(&interface);
					panel_bar_activate_button(panel, 3);
				}
					break;
				case SDLK_5: {
					panel_bar_t *panel = interface_get_panel_bar(&interface);
					panel_bar_activate_button(panel, 4);
				}
					break;

					/* Game speed */
				case SDLK_PLUS:
				case SDLK_KP_PLUS:
					if (game.game_speed < 40) game.game_speed += 1;
					LOGI("main", "Game speed: %u", game.game_speed);
					break;
				case SDLK_MINUS:
				case SDLK_KP_MINUS:
					if (game.game_speed >= 1) game.game_speed -= 1;
					LOGI("main", "Game speed: %u", game.game_speed);
					break;
				case SDLK_0:
					game.game_speed = DEFAULT_GAME_SPEED;
					LOGI("main", "Game speed: %u", game.game_speed);
					break;
				case SDLK_p:
					if (game.game_speed == 0) game_pause(0);
					else game_pause(1);
					break;

					/* Audio */
				case SDLK_s:
					sfx_enable(!sfx_is_enabled());
					break;
				case SDLK_m:
					midi_enable(!midi_is_enabled());
					break;

					/* Misc */
				case SDLK_ESCAPE:
					if (BIT_TEST(interface.click, 7)) { /* Building road */
						interface_build_road_end(&interface);
					} else if (interface.clkmap != 0) {
						interface_close_popup(&interface);
					}
					break;

					/* Debug */
				case SDLK_g:
					interface.viewport.layers ^= VIEWPORT_LAYER_GRID;
					break;
				case SDLK_j: {
					int current = 0;
					for (int i = 0; i < 4; i++) {
						if (interface.player == game.player[i]) {
							current = i;
							break;
						}
					}

					for (int i = (current+1) % 4; i != current; i = (i+1) % 4) {
						if (PLAYER_IS_ACTIVE(game.player[i])) {
							interface.player = game.player[i];
							LOGD("main", "Switched to player %i.", i);
							break;
						}
					}
				}
					break;
				case SDLK_z:
					if (event.key.keysym.mod & KMOD_CTRL) {
						save_game(0);
					}
					break;

				default:
					break;
				}
				break;
			case SDL_QUIT:
				game_loop_quit();
				break;
			}
		}

		unsigned int new_ticks = SDL_GetTicks();
		int delta_ticks = new_ticks - current_ticks;
		current_ticks = new_ticks;

		accum += delta_ticks;
		while (accum >= TICK_LENGTH) {
			game_update();

			/* Autosave periodically */
			if ((game.const_tick % AUTOSAVE_INTERVAL) == 0 &&
			    game.game_speed > 0) {
				int r = save_game(1);
				if (r < 0) LOGW("main", "Autosave failed.");
			}

			/* FPS */
			fps = 1000*((float)accum_frames / accum);
			if (fps_ema > 0) fps_ema = ema_alpha*fps + (1-ema_alpha)*fps_ema;
			else if (fps > 0) fps_ema = fps;

			if ((game.const_tick % (10*TICKS_PER_SEC)) == 0) {
				LOGV("main", "FPS: %i", fps_ema);
			}

			accum -= TICK_LENGTH;
			accum_frames = 0;
		}

		/* Update and draw interface */
		interface_update(&interface);

		interface.flags &= ~BIT(4);
		interface.flags &= ~BIT(7);

		gui_object_redraw((gui_object_t *)&interface, game.frame);

		/* TODO very crude dirty marking algortihm: mark everything. */
		sdl_mark_dirty(0, 0, sdl_frame_get_width(game.frame),
			       sdl_frame_get_height(game.frame));

		/* Swap video buffers */
		sdl_swap_buffers();

		accum_frames += 1;

		/* Reduce framerate to target */
		if (fps_target > 0) {
			int delay = 0;
			if (fps_ema > 0) delay = (1000/fps_target) - (1000/fps_ema);
			if (delay > 0) SDL_Delay(delay);
		}
	}
}
コード例 #5
0
ファイル: freeserf.c プロジェクト: athach/freeserf
/* game_loop() has been turned into a SDL based loop.
   The code for one iteration of the original game_loop is
   in game_loop_iter. */
static void
game_loop()
{
	/* FPS */
	int fps = 0;
	float fps_ema = 0;
	int fps_target = 25;
	/* TODO: compute alpha dynamically based on frametime */
	const float ema_alpha = 0.07;

	const int frametime_target = 1000 / fps_target; /* in milliseconds */
	int last_frame = SDL_GetTicks();

	int drag_button = 0;

	unsigned int last_down[3] = {0};
	unsigned int last_click[3] = {0};

	unsigned int current_ticks = SDL_GetTicks();
	unsigned int accum = 0;

	SDL_Event event;
	gui_event_t ev;

	game_loop_run = 1;
	while (game_loop_run) {
		while (SDL_PollEvent(&event)) {
			switch (event.type) {
			case SDL_MOUSEBUTTONUP:
				if (drag_button == event.button.button) {
					ev.type = GUI_EVENT_TYPE_DRAG_END;
					ev.x = event.button.x;
					ev.y = event.button.y;
					ev.button = drag_button;
					gui_object_handle_event((gui_object_t *)&interface, &ev);

					drag_button = 0;
				}

				ev.type = GUI_EVENT_TYPE_BUTTON_UP;
				ev.x = event.button.x;
				ev.y = event.button.y;
				ev.button = event.button.button;
				gui_object_handle_event((gui_object_t *)&interface, &ev);

				if (event.button.button <= 3 &&
				    current_ticks - last_down[event.button.button-1] < MOUSE_SENSITIVITY) {
					ev.type = GUI_EVENT_TYPE_CLICK;
					ev.x = event.button.x;
					ev.y = event.button.y;
					ev.button = event.button.button;
					gui_object_handle_event((gui_object_t *)&interface, &ev);

					if (current_ticks - last_click[event.button.button-1] < MOUSE_SENSITIVITY) {
						ev.type = GUI_EVENT_TYPE_DBL_CLICK;
						ev.x = event.button.x;
						ev.y = event.button.y;
						ev.button = event.button.button;
						gui_object_handle_event((gui_object_t *)&interface, &ev);
					}

					last_click[event.button.button-1] = current_ticks;
				}
				break;
			case SDL_MOUSEBUTTONDOWN:
				ev.type = GUI_EVENT_TYPE_BUTTON_DOWN;
				ev.x = event.button.x;
				ev.y = event.button.y;
				ev.button = event.button.button;
				gui_object_handle_event((gui_object_t *)&interface, &ev);

				if (event.button.button <= 3) last_down[event.button.button-1] = current_ticks;
				break;
			case SDL_MOUSEMOTION:
				if (drag_button == 0) {
					/* Move pointer normally. */
					interface_set_cursor(&interface, event.motion.x, event.motion.y);
				}

				for (int button = 1; button <= 3; button++) {
					if (event.motion.state & SDL_BUTTON(button)) {
						if (drag_button == 0) {
							drag_button = button;

							ev.type = GUI_EVENT_TYPE_DRAG_START;
							ev.x = event.motion.x;
							ev.y = event.motion.y;
							ev.button = drag_button;
							gui_object_handle_event((gui_object_t *)&interface, &ev);
						}

						ev.type = GUI_EVENT_TYPE_DRAG_MOVE;
						ev.x = event.motion.x;
						ev.y = event.motion.y;
						ev.button = drag_button;
						gui_object_handle_event((gui_object_t *)&interface, &ev);
						break;
					}
				}
				break;
			case SDL_KEYDOWN:
				if (event.key.keysym.sym == SDLK_q &&
				    (event.key.keysym.mod & KMOD_CTRL)) {
					game_loop_quit();
					break;
				}

				switch (event.key.keysym.sym) {
					/* Map scroll */
				case SDLK_UP: {
					viewport_t *viewport = interface_get_top_viewport(&interface);
					viewport_move_by_pixels(viewport, 0, -1);
				}
					break;
				case SDLK_DOWN: {
					viewport_t *viewport = interface_get_top_viewport(&interface);
					viewport_move_by_pixels(viewport, 0, 1);
				}
					break;
				case SDLK_LEFT: {
					viewport_t *viewport = interface_get_top_viewport(&interface);
					viewport_move_by_pixels(viewport, -1, 0);
				}
					break;
				case SDLK_RIGHT: {
					viewport_t *viewport = interface_get_top_viewport(&interface);
					viewport_move_by_pixels(viewport, 1, 0);
				}
					break;

					/* Panel click shortcuts */
				case SDLK_1: {
					panel_bar_t *panel = interface_get_panel_bar(&interface);
					panel_bar_activate_button(panel, 0);
				}
					break;
				case SDLK_2: {
					panel_bar_t *panel = interface_get_panel_bar(&interface);
					panel_bar_activate_button(panel, 1);
				}
					break;
				case SDLK_3: {
					panel_bar_t *panel = interface_get_panel_bar(&interface);
					panel_bar_activate_button(panel, 2);
				}
					break;
				case SDLK_4: {
					panel_bar_t *panel = interface_get_panel_bar(&interface);
					panel_bar_activate_button(panel, 3);
				}
					break;
				case SDLK_5: {
					panel_bar_t *panel = interface_get_panel_bar(&interface);
					panel_bar_activate_button(panel, 4);
				}
					break;

				case SDLK_TAB:
					if (event.key.keysym.mod & KMOD_SHIFT) {
						interface_return_from_message(&interface);
					} else {
						interface_open_message(&interface);
					}
					break;

					/* Game speed */
				case SDLK_PLUS:
				case SDLK_KP_PLUS:
					if (game.game_speed < 40) game.game_speed += 1;
					LOGI("main", "Game speed: %u", game.game_speed);
					break;
				case SDLK_MINUS:
				case SDLK_KP_MINUS:
					if (game.game_speed >= 1) game.game_speed -= 1;
					LOGI("main", "Game speed: %u", game.game_speed);
					break;
				case SDLK_0:
					game.game_speed = DEFAULT_GAME_SPEED;
					LOGI("main", "Game speed: %u", game.game_speed);
					break;
				case SDLK_p:
					if (game.game_speed == 0) game_pause(0);
					else game_pause(1);
					break;

					/* Audio */
				case SDLK_s:
					sfx_enable(!sfx_is_enabled());
					break;
				case SDLK_m:
					midi_enable(!midi_is_enabled());
					break;

					/* Video */
				case SDLK_f:
					if (event.key.keysym.mod & KMOD_CTRL) {
						sdl_set_fullscreen(!sdl_is_fullscreen());
					}
					break;

					/* Misc */
				case SDLK_ESCAPE:
					if (GUI_OBJECT(&interface.popup)->displayed) {
						interface_close_popup(&interface);
					} else if (interface.building_road) {
						interface_build_road_end(&interface);
					}
					break;

					/* Debug */
				case SDLK_g:
					interface.viewport.layers ^= VIEWPORT_LAYER_GRID;
					break;
				case SDLK_b:
					interface.viewport.show_possible_build = !interface.viewport.show_possible_build;
					break;
				case SDLK_j: {
					int current = 0;
					for (int i = 0; i < 4; i++) {
						if (interface.player == game.player[i]) {
							current = i;
							break;
						}
					}

					for (int i = (current+1) % 4; i != current; i = (i+1) % 4) {
						if (PLAYER_IS_ACTIVE(game.player[i])) {
							interface.player = game.player[i];
							LOGD("main", "Switched to player %i.", i);
							break;
						}
					}
				}
					break;
				case SDLK_z:
					if (event.key.keysym.mod & KMOD_CTRL) {
						save_game(0);
					}
					break;
				case SDLK_F10:
					interface_open_game_init(&interface);
					break;

				default:
					break;
				}
				break;
			case SDL_VIDEORESIZE:
				sdl_set_resolution(event.resize.w, event.resize.h, 0);

				frame_t *screen = sdl_get_screen_frame();
				int width = sdl_frame_get_width(screen);
				int height = sdl_frame_get_height(screen);

				screen_frame.clip.w = width;
				screen_frame.clip.h = height;

				gui_object_set_size((gui_object_t *)&interface, width, height);
				break;
			case SDL_QUIT:
				game_loop_quit();
				break;
			}
		}

		unsigned int new_ticks = SDL_GetTicks();
		int delta_ticks = new_ticks - current_ticks;
		current_ticks = new_ticks;

		/* Update FPS EMA per frame */
		fps = 1000*(1.0 / (float)delta_ticks);
		if (fps_ema > 0) fps_ema = ema_alpha*fps + (1-ema_alpha)*fps_ema;
		else if (fps > 0) fps_ema = fps;

		accum += delta_ticks;
		while (accum >= TICK_LENGTH) {
			game_update();

			/* Autosave periodically */
			if ((game.const_tick % AUTOSAVE_INTERVAL) == 0 &&
			    game.game_speed > 0) {
				int r = save_game(1);
				if (r < 0) LOGW("main", "Autosave failed.");
			}

			/* Print FPS */
			if ((game.const_tick % (10*TICKS_PER_SEC)) == 0) {
				LOGV("main", "FPS: %i", (int)fps_ema);
			}

			accum -= TICK_LENGTH;
		}

		/* Update and draw interface */
		interface_update(&interface);

		gui_object_redraw(GUI_OBJECT(&interface), game.frame);

		/* TODO very crude dirty marking algortihm: mark everything. */
		sdl_mark_dirty(0, 0, sdl_frame_get_width(game.frame),
			       sdl_frame_get_height(game.frame));

		/* Swap video buffers */
		sdl_swap_buffers();

		/* Reduce framerate to target if we finished too fast */
		int now = SDL_GetTicks();
		int frametime_spent = now - last_frame;

		if (frametime_spent < frametime_target) {
			SDL_Delay(frametime_target - frametime_spent);
		}
		last_frame = SDL_GetTicks();
	}
}