packages feed

apelsin-1.1: src/Toolbar.hs

module Toolbar where

import Graphics.UI.Gtk

import Control.Applicative
import Control.Exception
import Control.Monad
import Control.Monad.IO.Class
import Data.Maybe
import Data.Char (toLower)

import Network.Socket 
import Network.Tremulous.Protocol
import Network.Tremulous.Polling
import Network.Tremulous.MicroTime

import Types
import Constants
import STM2

import Config
import About
import Clanlist

getDNS :: String -> String -> IO (Maybe SockAddr)
getDNS host port = handle (\(_ :: IOException) -> return Nothing) $ do
	AddrInfo _ _ _ _ addr _ <- Prelude.head `liftM` getAddrInfo Nothing (Just host) (Just port)
	return $ Just addr

	
whileTrue :: Monad m => m Bool -> m ()
whileTrue f = f >>= \t -> when t (whileTrue f)

newToolbar :: Bundle -> [ClanHook] -> [PolledHook] -> [ClanPolledHook] -> IO HBox
newToolbar bundle@Bundle{..} clanHook polledHook bothHook = do		
	pb		<- progressBarNew
	set pb		[ widgetNoShowAll := True ]
	
	refresh		<- mkToolButton "Refresh all servers" stockRefresh "Ctrl+R or F5"
	clansync	<- mkToolButton "Sync clan list" stockSave "Ctrl+S or F6"		
	about		<- mkToolButton "About" stockAbout "F7"
		
	let doSync = newClanSync bundle clansync clanHook bothHook
	
	on about buttonActivated $ newAbout parent
	on clansync buttonActivated $ doSync
			
	align		<- alignmentNew 0 0 0 0
	alignbox	<- hBoxNew False spacing
	set align [ containerChild := alignbox ]
	
	boxPackStartDefaults alignbox refresh
	boxPackStartDefaults alignbox clansync
	boxPackStartDefaults alignbox about
			
	pbrbox		<- hBoxNew False spacing
	set pbrbox [ containerBorderWidth := spacing ]
	boxPackStart pbrbox align PackNatural 0
	boxPackStart pbrbox pb PackGrow 0
	
	
	let serverRefresh = do
		progressBarSetFraction pb 0
		widgetShow pb
		refresh `set` [ widgetSensitive := False ]
		Config {masterServers, delays=delays@Delay{..}} <- atomically $ readTMVar mconfig
		
		start <- getMicroTime

		-- This is a stupid guess based on that about 110 servers will respond and the master
		-- will take about 200ms to respond
		PollResult { serversResponded } <- atomically $ readTMVar mpolled
		let serversGuess = if serversResponded == 0 then 110 else serversResponded
		let tremTime = (packetDuplication + 1) * packetTimeout
			+ serversGuess * throughputDelay + 200 * 1000
		
		forkIO $ do
			hosts <- catMaybes <$> mapM
				(\(host, port, proto) -> fmap (`MasterServer` proto) <$> getDNS host (show port))
				masterServers
			pbth <- forkIO $ whileTrue $ do
				threadDelay 10000 --10 ms, 100 fps
				now <- getMicroTime
				let diff = now - start
				if now-start > fromIntegral tremTime then do
					postGUISync $ progressBarSetFraction pb 1
					return False
				else do
					postGUISync $ progressBarSetFraction pb
						(fromIntegral diff / fromIntegral tremTime)
					return True
				
			result <- pollMasters delays hosts

			atomically $ replaceTMVar mpolled result
			killThread pbth
			clans <- atomically $ readTMVar mclans
			postGUISync $ do
				mapM_ ($ result) polledHook
				mapM_ (\f -> f clans result) bothHook
				refresh `set` [ widgetSensitive := True ]
				widgetHide pb
		return ()
	on refresh buttonActivated serverRefresh

	Config {..} <- atomically $ readTMVar mconfig
	when autoMaster serverRefresh
	when autoClan doSync

	on parent keyPressEvent $  do
		kmod	<- eventModifier
		k	<- map toLower <$> eventKeyName
		case kmod of
			[Control]
				| k == "r" -> liftIO serverRefresh	>> return True
				| k == "s" -> liftIO doSync		>> return True
			[]	| k == "f5" -> liftIO serverRefresh	>> return True
			[]	| k == "f6" -> liftIO doSync		>> return True
			[]	| k == "f7" -> liftIO (newAbout parent)	>> return True
			_ -> return False 				
	
	return pbrbox
	where mkToolButton lbl icon tip = do
		button <- buttonNewWithLabel lbl
		set button	[ buttonImage		:=> imageNewFromStock icon IconSizeButton
				, buttonRelief		:= ReliefNone 
				, buttonFocusOnClick	:= False
				, widgetTooltipText	:= Just tip ]
		return button