/*
    Tucnak - VHF contest log
    Copyright (C) 2002-2023  Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/


#include "header.h"
#include "bfu.h"
#include "cwwindow.h"
#include "fifo.h"
#include "fonts.h"
#include "icons.h"
#include "kbd.h"
#include "main.h"
#include "map.h"
#include "misc.h"
#include "qsodb.h"
#include "rc.h"
#include "rotar.h"
#include "tsdl.h"
#include "session.h"
#include "ssbd.h"
#include "subwin.h"
#include "terminal.h"
#include "zosk.h"
#include "zpng.h"

#define DEBUG_REDRAWx
#undef TESTCOLOR

#ifdef Z_HAVE_SDL

#ifdef Z_ANDROID
#define SDLTESTx
#endif
//#define SDLTEST
	
struct tsdl *sdl;
struct zzsdl *zsdl;

SDL_Color pal[256];


#ifdef SDLTEST
int sdl_test(void);
#endif

#if defined(Z_MSC_MINGW) && defined(Z_HAVE_SDL1)
int getaltstate(){
	SHORT ret = GetKeyState(VK_MENU);// Alt key
	return (ret & 0x8000) != 0;// high-order bit is 1
}
#endif

#ifdef Z_HAVE_SDL_TTF
#include "fonts.inc"
#endif

char *dumpmods(SDL_Keysym *sym){
	static GString *gs = NULL;
	if (gs == NULL) gs = g_string_sized_new(256);
	g_string_truncate(gs, 0);

	SDL_Keymod mod;
	if (sym != NULL)
		mod = sym->mod;
	else
		mod = SDL_GetModState();

	if (mod & KMOD_LSHIFT) g_string_append(gs, "LSHIFT ");
	if (mod & KMOD_RSHIFT) g_string_append(gs, "RSHIFT ");
	if (mod & KMOD_LCTRL) g_string_append(gs, "LCTRL ");
	if (mod & KMOD_RCTRL) g_string_append(gs, "RCTRL ");
	if (mod & KMOD_LALT) g_string_append(gs, "LALT ");
	if (mod & KMOD_RALT) g_string_append(gs, "RALT ");
#ifdef Z_HAVE_SDL2
	if (mod & KMOD_LGUI) g_string_append(gs, "LGUI ");
	if (mod & KMOD_RGUI) g_string_append(gs, "RGUI ");
#endif
	if (mod & KMOD_NUM) g_string_append(gs, "NUM ");
	if (mod & KMOD_CAPS) g_string_append(gs, "CAPS ");
	if (mod & KMOD_MODE) g_string_append(gs, "MODE ");
#if defined(Z_MSC_MINGW) && defined(Z_HAVE_SDL1)
	if (getaltstate()) g_string_append(gs, "VK_MENU ");
#endif
	return gs->str;
}

struct tsdl *init_sdl(){
    int w, h, ty;
    SDL_Rect r;
//char errbuf[1024];
    SDL_Surface *tucnak64 = NULL;
    time_t now;
    struct tm *tm;
#ifdef Z_HAVE_SDL1
	const SDL_VideoInfo *vi;
#endif
#ifdef Z_HAVE_SDL2
	SDL_DisplayMode dm;
#endif
 
#ifdef SDLTEST
    exit(sdl_test());
#endif

    dbg("init_sdl\n"); 
	
	//SDL_putenv("SDL_VIDEO_WINDOW_POS=0,0");
	//SDL_putenv("SDL_VIDEO_WINDOW_POS=center");

    if (opt_g && opt_t) {
        opt_t=opt_g=0;   /* user is crazy */
    }

    if (!opt_g && !opt_t){
        if (getenv("DISPLAY")!=NULL) opt_g=1;
    }else{
        if (opt_t) opt_g=0;
    }
    
    if (!opt_g) return NULL;

    
    time(&now);
    tm=localtime(&now);
    now=(tm->tm_mon<<4)+tm->tm_mday - 4;
    


    sdl=g_new0(struct tsdl, 1);
	sdl->old_mouse_x = -1;
	sdl->old_mouse_y = -1;
#ifdef Z_HAVE_SDL1
	sdl->iconvhandle = iconv_open("ISO8859-2", "UCS-2LE");
#endif
#ifdef Z_HAVE_SDL2
	sdl->iconvhandle = iconv_open("ISO8859-2", "UTF-8");
#endif
	if (sdl->iconvhandle == (iconv_t)(-1)){
        int err=errno;
        char *c = z_strdup_strerror(err);
        dbg("Can't init iconv %s\n", c);
        g_free(c);
        free_sdl();
        return NULL;
    }
    
    
    setenv("SDL_VIDEO_X11_WMCLASS", "Tucnak", 1);
    if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE)){
        log_adds("Can't set graphics mode (SDL_Init)");
        free_sdl();
        return NULL;
    };  
    sdl_pre_info();
#ifdef Z_HAVE_SDL1
    vi = SDL_GetVideoInfo();
    if (vi){
        sdl->screen_w = vi->current_w;
        sdl->screen_h = vi->current_h;
    }
#endif
#ifdef Z_HAVE_SDL2
	if (SDL_GetCurrentDisplayMode(0/*TODO*/, &dm) == 0){
		sdl->screen_w = dm.w;
		sdl->screen_h = dm.h;
	}
#endif
	sdl->window_w = cfg->gfx_x;
	sdl->window_h = cfg->gfx_y;
	zsdl_sys_key_repeat(300, 20);
#ifdef Z_HAVE_SDL1
    SDL_EnableUNICODE(1);
#endif
    

#ifdef Z_HAVE_LIBPNG   
    if ((now&0xf8) == 192){
        sdl->icon = zpng_create(icon_tucnakv, sizeof(icon_tucnakv));
        tucnak64 = zpng_create(icon_tucnakv64, sizeof(icon_tucnakv64));
    }else{
        sdl->icon = zpng_create(icon_tucnak, sizeof(icon_tucnak));
        tucnak64 = zpng_create(icon_tucnak64, sizeof(icon_tucnak64));
    }
	sdl->home = zpng_create(icon_home32, sizeof(icon_home32));
	sdl->mast = zpng_create(icon_mast, sizeof(icon_mast));
	sdl->zoomin = zpng_create(icon_zoomin32, sizeof(icon_zoomin32));
	sdl->zoomout = zpng_create(icon_zoomout32, sizeof(icon_zoomout32));
	sdl->key = zpng_create(icon_key32, sizeof(icon_key32));
	sdl->xfer = zpng_create(icon_xfer32, sizeof(icon_xfer32));

    if (!sdl->icon) zinternal("Can't create icon_tucnak, corrupted executable?");
    if (!tucnak64) zinternal("Can't create icon_tucnak64, corrupted executable?");
    if (!sdl->home) zinternal("Can't create icon_home32, corrupted executable?");
    if (!sdl->zoomin) zinternal("Can't create icon_zoomin32, corrupted executable?");
    if (!sdl->zoomout) zinternal("Can't create icon_zoomout32, corrupted executable?");
    if (!sdl->key) zinternal("Can't create icon_key, corrupted executable?");
    if (!sdl->xfer) zinternal("Can't create icon_xfer, corrupted executable?");
#endif
	if (z_pipe(sdl->eventpipe)) {
        zinternal("can't create pipe for internal communication");
    }
    /*dbg("sdl_attach terminal: pipe %d->%d\n", fd[1], fd[0]);*/
    z_sock_nonblock(sdl->eventpipe[0], 1);

    MUTEX_INIT(sdl->eventpipestate);

    w=cfg->gfx_x;
    h=cfg->gfx_y;
	if (cfg->fullscreen){
		w = sdl->screen_w;
		h = sdl->screen_h;
	}
    if (sdl_setvideomode(w, h, 1)){
        log_adds("Can't set graphics mode");
        free_sdl(); 
        return NULL;
    }
    sdl->bpp = sdl->screen->format->BitsPerPixel;
    
    ty = sdl->screen->h / 2 - 2 * zsdl->font_h;
	if (tucnak64){
		r.x=(sdl->screen->w-64)/2;
		r.y=sdl->screen->h/2 - 2 * zsdl->font_h - 64;
		r.w=24;
		r.h=24;
		SDL_BlitSurface(tucnak64, NULL, sdl->screen, &r);
        ty -= 64;
        SDL_FreeSurface(tucnak64);
	}
    ty -= zsdl->font_h;
    zsdl_printf(sdl->screen, sdl->screen->w / 2, ty, sdl->gr[12], 0, ZFONT_CENTERX, "Tucnak %s", VERSION);
    SDL_UpdateRect(sdl->screen, 0, 0, w, h);
    rot_update_colors();
#ifdef Z_HAVE_LIBPNG   
	SDL_SetWindowIcon(zsdl->window, sdl->icon);
#endif
    
    /*{
        r.w = 256;
        r.h = 256;
        r.x = 0;
        r.y = 0;
        SDL_Surface *ver = SDL_CreateRGBSurface(SDL_SWSURFACE, 256, 256, sdl->bpp, sdl->screen->format->Rmask, sdl->screen->format->Gmask, sdl->screen->format->Bmask, 0);
        SDL_Surface *hor = SDL_CreateRGBSurface(SDL_SWSURFACE, 256, 256, sdl->bpp, sdl->screen->format->Rmask, sdl->screen->format->Gmask, sdl->screen->format->Bmask, 0);
        error("scr=%p %x %x %x %x \n", sdl->screen, sdl->screen->format->Aloss, sdl->screen->format->Ashift, sdl->screen->format->Amask, sdl->screen->format->alpha);
        error("hor=%p %x %x %x %x \n", hor, hor->format->Aloss, hor->format->Ashift, hor->format->Amask, hor->format->alpha);
        error("ver=%p %x %x %x %x \n", ver, ver->format->Aloss, ver->format->Ashift, ver->format->Amask, ver->format->alpha);
        int ii, jj;
        for (ii=0; ii<256;ii++)
            for (jj=0;jj<256;jj++){
                z_putpixel(hor, ii, jj, z_makecol(ii, ii, ii));
                z_putpixel(ver, ii, jj, z_makecol(jj, jj, jj));
            }
        SDL_BlitSurface(hor, NULL, sdl->screen, &r);

        r.x = 258;
        SDL_BlitSurface(ver, NULL, sdl->screen, &r);
        

        r.x = 516;
        SDL_SetAlpha(ver, SDL_SRCALPHA, 128);
        error("ver=%p %x %x %x %x \n", ver, ver->format->Aloss, ver->format->Ashift, ver->format->Amask, ver->format->alpha);
        SDL_BlitSurface(hor, NULL, sdl->screen, &r);
        SDL_UpdateRect(sdl->screen, 0, 0, 0, 0);
        sleep(3);
        SDL_BlitSurface(ver, NULL, sdl->screen, &r);
        
        SDL_UpdateRect(sdl->screen, 0, 0, 0, 0);
        sleep(4);
        
    } */
    return sdl;
}

void sdl_stop_event_thread(){
    dbg("sdl_stop_event_thread\n");
    if (!sdl) return;
    if (!sdl->event_thread) return;
	if (!sdl->ignore_progress) progress(VTEXT(T_WAIT_SDL_THR));		
	sdl->ignore_progress = 0;
    sdl->event_thread_break=1;
	g_thread_join(sdl->event_thread);
    sdl->event_thread=0;
}

void free_sdl(){
    if (!sdl) return;
#ifdef EVENTS_IN_SAME_THREAD
	if (sdl->event_timer_id) zselect_timer_kill(zsel, sdl->event_timer_id);
#endif

    sdl_stop_event_thread();
	if (sdl->mast) SDL_FreeSurface(sdl->mast);
    if (sdl->icon) SDL_FreeSurface(sdl->icon);
    if (sdl->screen) SDL_FreeSurface(sdl->screen);
    if (sdl->key) SDL_FreeSurface(sdl->key);
    if (sdl->xfer) SDL_FreeSurface(sdl->xfer);
    if (sdl->xfer) SDL_FreeSurface(sdl->home);
    if (sdl->xfer) SDL_FreeSurface(sdl->zoomin);
    if (sdl->xfer) SDL_FreeSurface(sdl->zoomout);
#ifdef Z_HAVE_SDL_TTF
    if (sdl->ttf_rwops != NULL) SDL_RWclose(sdl->ttf_rwops);
#endif
    MUTEX_FREE(sdl->eventpipestate);
    zg_free0(sdl->title);

    SDL_Quit();

	if (sdl->iconvhandle) iconv_close(sdl->iconvhandle);
    
    g_free(sdl);
    sdl=NULL;
}

char *sdl_best = NULL;

void sdl_pre_info(){
	GString *gs = g_string_new("");

#ifdef Z_HAVE_SDL1
	const SDL_VideoInfo *vi = SDL_GetVideoInfo();
	if (!vi){
		g_string_append_printf(gs, "No \"best mode\" exists\n");
	}
	else{
		g_string_append_printf(gs, "Screen: %dx%d-%d ", vi->current_w, vi->current_h, vi->vfmt->BitsPerPixel);
		if (vi->vfmt->palette){
			g_string_append_printf(gs, "using palette ");
		}
		else{
			g_string_append_printf(gs, "R=%x G=%x B=%x A=%x ", vi->vfmt->Rmask, vi->vfmt->Gmask, vi->vfmt->Bmask, vi->vfmt->Amask);
		}
		g_string_append_printf(gs, "colorkey=%04x alpha=%d\n", vi->vfmt->colorkey, vi->vfmt->alpha);
	}
#endif
#ifdef Z_HAVE_SDL2
	SDL_DisplayMode dm;
	int i;
	for (i = 0; i < SDL_GetNumVideoDisplays(); i++){
		if (SDL_GetCurrentDisplayMode(i, &dm) == 0){
			g_string_append_printf(gs, "Screen %d: %dx%d, %d Hz, pixel format %s \n", i, dm.w, dm.h, dm.refresh_rate, SDL_GetPixelFormatName(dm.format));
		}
	}
#endif

   
	g_free(sdl_best);
	sdl_best = g_strdup(gs->str);
	g_string_free(gs, TRUE);
}

void sdl_info(GString *gs){

	g_string_append_printf(gs, "\n  sdl_info:\n");


	if (SDL_Init(SDL_INIT_VIDEO) < 0){
		g_string_append_printf(gs, "SDL_Init failed\n");
		return;
	};

#ifdef Z_HAVE_SDL1
	char str[256];
	SDL_VideoDriverName(str, sizeof(str)-1);
	str[sizeof(str) - 1] = '\0';
#endif
#ifdef Z_HAVE_SDL2
	const char *str = SDL_GetCurrentVideoDriver();
#endif

    if (*str) g_string_append_printf(gs, "Driver: %s\n", str);

	if (!sdl_best) sdl_pre_info();
    g_string_append(gs, sdl_best);
#ifdef Z_HAVE_SDL1
	int oldw, oldh;
	int i;
	SDL_Rect **modes;

    modes=SDL_ListModes(NULL, SDL_FULLSCREEN);
    if (!modes) {
        g_string_append_printf(gs, "No video modes available (SDL_ListModes)\n");
    }else{
        if (modes==(SDL_Rect**)-1){
            g_string_append_printf(gs, "All resolutinons available\n");
            return;
        }
        
		g_string_append_printf(gs, "Modes:  ");
        oldw=oldh=-1;
        for (i=0;modes[i];i++){
            if (modes[i]->w==oldw && modes[i]->h==oldh) continue;
            g_string_append_printf(gs, "%dx%d ", modes[i]->w, modes[i]->h);
            oldw=modes[i]->w;
            oldh=modes[i]->h;
        }
	    g_string_append_printf(gs, "\n");
    }
#endif
#ifdef Z_HAVE_SDL2
	/*SDL_DisplayMode dm = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
	for (int di = 0; di < SDL_GetNumVideoDisplays(); di++){
		g_string_append_printf(gs, "Display %d\n", di);

		for (int mi = 0; mi < SDL_GetNumDisplayModes(di); mi++){
			if (SDL_GetDisplayMode(di, mi, &dm) != 0) continue;
			
			g_string_append_printf(gs, "Mode %d: %dx%d, %d Hz, pixel format %s \n", mi, dm.w, dm.h, dm.refresh_rate, SDL_GetPixelFormatName(dm.format));
		}
	}*/
#endif
	g_string_append_printf(gs, "\n");
}


#ifdef EVENTS_IN_SAME_THREAD
void sdl_event_timer(void *xxx){
	if (!sdl) return;
	SDL_PumpEvents();
	sdl->event_timer_id = zselect_timer_new(zsel, 50, sdl_event_timer, NULL);
}
#endif



int sdl_setvideomode(int w, int h, int init_x){
    int i, flags;
    
    sdl_stop_event_thread();


	if (init_x == 0) cfg->maximized = zsdl_maximized(zsdl, NULL, NULL);
    

	flags = SDL_SWSURFACE;
#ifdef Z_HAVE_SDL1
	flags |= SDL_RESIZABLE | SDL_HWPALETTE;
#endif

	if (cfg->fullscreen){
#ifdef Z_HAVE_SDL1
		flags |= SDL_FULLSCREEN;
#endif
#ifdef Z_HAVE_SDL2
		flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
#endif
		if (sdl->screen_w > 0 && sdl->screen_h >= 0){
            w = sdl->screen_w;
            h = sdl->screen_h;
#ifdef Z_ANDROID
            if (cfg->portrait == 0 && w < 640){  // for 480x320 use libSDL's OpenGL resize
                w = 640;
                h = 480;
            }
#endif
        }
    }else{
#ifdef Z_HAVE_SDL2
		flags |= SDL_WINDOW_RESIZABLE;
#endif
		sdl->window_w = w;
		sdl->window_h = h;
	}
    /*w = 480;
    h = 800;*/
#ifdef Z_HAVE_SDL1
	dbg("SDL_SetVideoMode(%d,%d)\n", w, h);
	sdl->screen = SDL_SetVideoMode(w, h, 0, flags);
	if (sdl->screen == NULL){
		dbg("SDL_SetVideoMode(%d,%d) failed\n", w, h);
		return 1;    
	}
	zsdl = zsdl_init(sdl->screen, cfg->fontheight, cfg->inverse, NULL);

#endif
#ifdef Z_HAVE_SDL2
	dbg("SDL_CreateWindow(%d,%d)\n", w, h);

	/*if (zsdl != NULL && zsdl->window != NULL){
		SDL_DestroyWindow(zsdl->window);
		zsdl->window = NULL;
	}
	
	SDL_Window *win = SDL_CreateWindow("Tucnak "PACKAGE_VERSION, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, flags);
	if (win == NULL){
		dbg("SDL_CreateWindow(%d,%d) failed\n", w, h);
		return 1;
	} */

	SDL_Window *win = NULL;
	if (zsdl != NULL) win = zsdl->window;

	if (zsdl == NULL || zsdl->window == NULL){
		win = SDL_CreateWindow("Tucnak "PACKAGE_VERSION, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, flags);
		if (win == NULL){
			dbg("SDL_CreateWindow(%d,%d) failed\n", w, h);
			return 1;
		}
	}else{
		SDL_SetWindowFullscreen(zsdl->window, flags);
	}
	

	Uint32 format = SDL_GetWindowPixelFormat(win);
	SDL_PixelFormat *pxformat = SDL_AllocFormat(format);

	//sdl->screen = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, pxformat->BitsPerPixel, pxformat->Rmask, pxformat->Gmask, pxformat->Bmask, pxformat->Amask);
	sdl->screen = SDL_GetWindowSurface(win);

	
	/*SDL_SetWindowResizable(zsdl->window, SDL_TRUE);
	if (cfg->fullscreen){
		SDL_SetWindowFullscreen(zsdl->window, SDL_TRUE);
	}*/
	//SDL_StartTextInput();
#endif

#ifdef Z_HAVE_SDL_TTF
    sdl->ttf_rwops = SDL_RWFromConstMem(font_JetBrainsMono_Regular, sizeof(font_JetBrainsMono_Regular));
	zsdl = zsdl_init(sdl->screen, cfg->fontheight, cfg->inverse, sdl->ttf_rwops);
#else
	zsdl = zsdl_init(sdl->screen, cfg->fontheight, cfg->inverse, NULL);
#endif

#ifdef Z_HAVE_SDL2
    zsdl->window = win;
    zsdl->format = format;
    zsdl->pxformat = pxformat;
#endif


    //if (SDL_MUSTLOCK(sdl->screen)) zinternal("surface must be locked");
    sdl->termarea.x = 0;
    sdl->termarea.y = 0;
    sdl->termarea.w = w;
    sdl->termarea.h = h;
    
    if (sdl->screen->format->palette){
        int i;
        SDL_PixelFormat *fmt;
        
        for (i=0; i<256; i++){
            sdl->colors[i].r=(i&0xe0)+31; 
            sdl->colors[i].g=((i<<3)&0xe0)+31;
            sdl->colors[i].b=((i<<6)&0xc0)+63;
        }
#ifdef Z_HAVE_SDL1
        SDL_SetColors(sdl->screen, sdl->colors, 0, 256);
#endif
#ifdef Z_HAVE_SDL2
		SDL_SetPaletteColors(zsdl->screen->format->palette, sdl->colors, 0, 256);
#endif
		fmt = sdl->screen->format;
        fmt->Rmask=0xe0;
        fmt->Gmask=0x1c;
        fmt->Bmask=0x03;
        fmt->Rshift=5;
        fmt->Gshift=2;
        fmt->Bshift=0;
        fmt->Rloss=5;
        fmt->Gloss=5;
        fmt->Bloss=6;
    }
    pal[0].r = pal[0].g = pal[0].b = 0;
    sdl->gr[0] = z_makecol(0, 0, 0);
    for (i=1; i<16; i++){
        pal[i].r = pal[i].g = pal[i].b = i*4+3;
        sdl->gr[i] = z_makecol(i*16+15, i*16+15, i*16+15);
    }
    for (i=0; i<16; i++){
        int r,g,b;
        /*if (i == 6){ //DARKCYAN KST hack
            r = 212;
            g = 212;
            b = 0; 
        }else*/ if (i==0x08){
			r=g=b=96;
		}else if (i&0x08){
            r=255*(i&0x01);
            g=255*((i&0x02)>>1);
            b=255*((i&0x04)>>2);
        }else if (i==0x07){
            r=192;
            g=192;
            b=192;
        }else {
            r=128*(i&0x01);
            g=128*((i&0x02)>>1);
            b=128*((i&0x04)>>2);
        }

        sdl->termcol[i]=z_makecol(r,g,b);
        /*dbg("color[%d]=%d,%d,%d\n", i, r,g,b);*/
    }
    sdl->cursor=z_makecol(128,128,0);
    sdl->yellow=z_makecol(255,255,0);
    sdl->yellow2=z_makecol(224,224,0);
    sdl->green=z_makecol(0,255,0);
    sdl->red=z_makecol(255,0,0);
	sdl->magenta = z_makecol(255, 0, 255);

	sdl->event_thread_break=0;
    //dbg("creating SDL thread\n");
    sdl->event_thread = g_thread_try_new("SDL events", sdl_event_thread, GINT_TO_POINTER(sdl->eventpipe[1]), NULL);
	if (!sdl->event_thread) {
        error("ERROR: can't create event thread");
		sdl_stop_event_thread();
        return -1;
    }
    //dbg("created SDL thread");
	SDL_FillRect(sdl->screen, NULL, 0);
	SDL_UpdateRect(sdl->screen, 0, 0, sdl->screen->w, sdl->screen->h); // fill space between char area and screen boundary
	//sleep(3);
    
#ifdef EVENTS_IN_SAME_THREAD
	if (!sdl->event_timer_id) sdl->event_timer_id = zselect_timer_new(zsel, 100, sdl_event_timer, NULL);
#endif

#ifdef AUTOSAVE
    if (init_x == 0 && gses != NULL) {
        menu_save_rc(NULL);
    }
#endif
    return 0;
}

#ifdef DEBUG_REDRAW
struct timeval startx = {0, 0};
#endif

#define ISSEL(x) ((x) >= gses->sel_start && (x) <= gses->sel_stop)


void sdl_redraw_screen(void){
    int x,y,p,fg,bg,d,i,j, col;
    int minx,maxx,miny,maxy;
    unsigned char c;
    SDL_Rect dstrect, r;
	int update_dstrect=0;
    int iconx=0, icony=0;
    int draw_icon=0;
    int ll;
	int selcol;
	int flg;
    int doupdate = 1;


	if (!term || !term->x) return;

    if (gses && gses->osk) doupdate = 0;
	sdl->progress_w = 0;
#ifdef DEBUG_REDRAW
    if (startx.tv_sec!=0){
        struct timeval stopx;
        int sec, usec;
        gettimeofday(&stopx, NULL);
        usec=stopx.tv_usec-startx.tv_usec;
        sec=stopx.tv_sec-startx.tv_sec;
        if (usec<0){usec+=1000000;sec--;}
//        dbg("a %d.%06d %d.%06d\n", startx.tv_sec % 100, startx.tv_usec, stopx.tv_sec % 100, stopx.tv_usec);
        //dbg("sdl_redraw_screen: %d.%06d\t", sec, usec);
    }
    gettimeofday(&startx, NULL);
#endif
	// TODO při ukončování pod msvc tady někdy je term == NULL
    minx=term->x;
    miny=term->y;
    maxx=-1;
    maxy=-1;

    if (term->last_screen!=NULL && (term->cx!=term->lcx || term->cy!=term->lcy)){
        term->last_screen[term->cx+term->cy*term->x]=0;
        if (term->lcx >= 0 && term->lcy >= 0 && term->lcx < term->x && term->lcy < term->y) term->last_screen[term->lcx+term->lcy*term->x]=0;
        term->lcx=term->cx;
        term->lcy=term->cy;
    }
	
	if (gses && gses->ontop && gses->ontop->screen && !show_qs()){

		dstrect.x=gses->ontop->x*FONT_W;
		dstrect.y=gses->ontop->y*FONT_H;
		dstrect.w=gses->ontop->w*FONT_W;
		dstrect.h=gses->ontop->h*FONT_H;
		SDL_BlitSurface(gses->ontop->screen, 0, sdl->screen,&dstrect);
		if (gses->ontop->type == SWT_MAP){
			sw_map_redraw_icons(gses->ontop, &dstrect, 0);
		}
		//dbg("blit gses->ontop->screen    pixel=%d\n", z_getpixel(sdl->screen, gses->ontop->x*FONT_W, gses->ontop->y*FONT_H)); 
    	fill_lastarea(gses->ontop->x, gses->ontop->y, gses->ontop->w, gses->ontop->h, 0);
		update_dstrect=1;
			//SDL_UpdateRect(sdl->screen, 0, 0, 0, 0); 
	}
	if (gses && gses->ontop && gses->focused && gses->win->prev == gses->win->next) {
		dstrect.x = gses->ontop->x * FONT_W - 1;
		dstrect.y = gses->ontop->y * FONT_H - 1;
		dstrect.w = gses->ontop->ww * FONT_W + 2;
		dstrect.h = gses->ontop->hh * FONT_H + 2;
		z_rect2(sdl->screen, &dstrect, sdl->cursor);
		update_dstrect=1;
	}
	/*if (gses && gses->sel_max >= 0 && gses->sel_min != INT32_MAX){
        dbg("clearing %d %d\n", gses->sel_min, gses->sel_max);
		for (i = gses->sel_min; i <= gses->sel_max; i++){
			if (i < 0) continue;
			if (i >= term->x * term->y) break;
			term->last_screen[i] = 0;
		}
        gses->sel_min = INT32_MAX;
        gses->sel_max = -1;
	} */

    if (gses && gses->icon != gses->oldicon){
        gses->oldicon=gses->icon;
        iconx=1;
        icony=term->y - cfg->loglines - DISP_QSOS - 1;
		if (ctest && !ctest->oldcontest) icony-=ctest->spypeers->len;
		//dbg(" gses->icon ");
        fill_lastarea(iconx, icony, 48/FONT_W+1, 48/FONT_H+1, 0);
        draw_icon=1;
    }
#ifdef HAVE_SNDFILE123
    if (gses && gses->icon == ssbd->recicon /* && ssbd->loglevel != ssbd->oldloglevel*/){
        x=term->x-1;
        /*for (y=0;y<6;y++) {
//            term->last_screen[x+term->x*y]=0;
            set_char(x, y, '2' + COL_NORM);
//            term->screen[x+term->x*y]='2';
        } */
		//dbg(" gses->volume ");
    }
#endif
    d=0;
    for (p=0,y=0;y<term->y;y++){
        for (x=0;x<term->x;x++,p++){
            if (term->screen[p] == term->last_screen[p]) continue;
			if (!term->screen[p]) continue;
            d=1;
            if (x<minx) minx=x;
            if (y<miny) miny=y;
            if (x>maxx) maxx=x;
            if (y>maxy) maxy=y;
                    
            fg=(term->screen[p]&0x0700)>>8;
            if (term->screen[p]&0x4000) fg|=0x08;
            if (term->screen[p]&ATTR_FRAME){
                c=term->screen[p]-48;                
                if (c==170) c=134;
                else if (c==169) c=133;
            }else{
                c=term->screen[p];
            }
            
            bg=sdl->termcol[(term->screen[p]&0x3800)>>11];
            if (x+y>0 && x==term->cx && y==term->cy) bg=sdl->cursor;
            
			if (gses && ISSEL(p)) bg = z_makecol(0, 75, 75);
			flg = ZFONT_CHAR;
			if ((term->screen[p] >> 15) & DOUBLEHT_MASK) 
				flg |= ZFONT_DOUBLEHT;
			if ((term->screen[p] >> 15) & DOUBLEHB_MASK) 
				flg |= ZFONT_DOUBLEHB;

		    SDL_SetClipRect(sdl->screen, &sdl->termarea);
            zsdl_printf(sdl->screen, x*FONT_W, y*FONT_H, 
                    sdl->termcol[fg],
                    bg,
                    flg,
                    (char *)&c);

			if (gses && ISSEL(p)){
				selcol = z_makecol(0, 150, 150);
				if (SDL_MUSTLOCK(sdl->screen)) SDL_LockSurface(sdl->screen);
				if (!ISSEL(p - 1) || (x % term->x) == 0) z_line(sdl->screen, x * FONT_W, y * FONT_H, x * FONT_W, y * FONT_H + FONT_H - 1, selcol);
				if (!ISSEL(p + 1) || (x % term->x) == term->x - 1) z_line(sdl->screen, x * FONT_W + FONT_W - 1, y * FONT_H, x * FONT_W + FONT_W - 1, y * FONT_H + FONT_H - 1, selcol);
				if (!ISSEL(p - term->x)) z_line(sdl->screen, x * FONT_W, y * FONT_H, x * FONT_W + FONT_W - 1, y * FONT_H, selcol);
				if (!ISSEL(p + term->x)) z_line(sdl->screen, x * FONT_W, y * FONT_H + FONT_H - 1, x * FONT_W + FONT_W - 1, y * FONT_H + FONT_H - 1, selcol);
				if (SDL_MUSTLOCK(sdl->screen)) SDL_UnlockSurface(sdl->screen);
			}
#ifdef HAVE_SNDFILE
            gssbd->oldloglevel = -42;
#endif
        }
    }
	// clear borders behind char area
	for (x = term->x * FONT_W; x < sdl->screen->w; x++)
		for (y = 0; y < sdl->screen->h; y++)
			z_putpixel(sdl->screen, x, y, 0);
	for (y = term->y * FONT_H; y < sdl->screen->h; y++)
		for (x = 0; x < sdl->screen->w; x++)
			z_putpixel(sdl->screen, x, y, 0);

#ifdef HAVE_SNDFILE
    MUTEX_LOCK(gssbd->loglevel);
    if (gses && gses->icon == gssbd->recicon && gssbd->loglevel != gssbd->oldloglevel){
        ll = gssbd->loglevel;
        MUTEX_UNLOCK(gssbd->loglevel);
        //dbg(" XXX   %d!=%d  \n", ll, ssbd->oldloglevel);
        x=(term->x-1)*FONT_W;
        y=6*FONT_H-1;
		if (SDL_MUSTLOCK(sdl->screen)) SDL_LockSurface(sdl->screen);
        for (i=0;i<96;i++) {
            if (i<ll) col=z_makecol(0, 255,0);
            else col=z_makecol(0, 100, 0);
            for (j=0;j<FONT_W-1;j++) z_putpixel(sdl->screen, x+j, y-i, col);
        }
		if (SDL_MUSTLOCK(sdl->screen)) SDL_LockSurface(sdl->screen);
        r.x=x;
        r.y=0;
        r.w=FONT_W;
        r.h=6*FONT_H;
        //dbg(" [%d,%d,%d,%d] ", r.x, r.y, r.w, r.h);
        
		if (doupdate) SDL_UpdateRect(sdl->screen, r.x, r.y, r.w, r.h);
        gssbd->oldloglevel = ll;
    }else{
        MUTEX_UNLOCK(gssbd->loglevel);
    }
#endif
#ifdef Z_HAVE_SDL
	if (gbat){
		x = (term->x - 2) * FONT_W;
        y = FONT_H;
		zbat_draw(gbat, sdl->screen, x, y, FONT_W - 1, FONT_H); 
		//if (doupdate) dbg("updaterect %p %d %d %d %d\n", sdl->screen, x, y, FONT_W - 1, FONT_H);
		if (doupdate) SDL_UpdateRect(sdl->screen, x, y, FONT_W - 1, FONT_H);
	}
#endif
    if (draw_icon) {
        
        r.x=iconx*FONT_W;
        r.y=icony*FONT_H;
        r.w=48;
        r.h=48;
        SDL_BlitSurface(gses->icon, NULL, sdl->screen, &r); 
		if (doupdate) SDL_UpdateRect(sdl->screen, r.x, r.y, r.w, r.h);
    }
    if (d) {
        minx=minx*FONT_W;
        miny=miny*FONT_H;
        maxx=(maxx+1)*FONT_W;
        maxy=(maxy+1)*FONT_H;
        /*rect(sdl->screen, minx, miny, maxx, maxy, 0x80);*/
		//dbg(" chars ");
        if (doupdate) SDL_UpdateRect(sdl->screen, minx, miny, maxx-minx, maxy-miny);
        memcpy(term->last_screen, term->screen, term->x * term->y * sizeof(int));
    }
	if (gses && gses->osk){
		SDL_Rect r;
		zosk_draw(gses->osk);
		r.x = gses->osk->parent_x;
		r.y = gses->osk->parent_y;
		r.w = gses->osk->surface->w;
		r.h = gses->osk->surface->h;
		SDL_BlitSurface(gses->osk->surface, NULL, sdl->screen, &r);
		SDL_UpdateRect(sdl->screen, 0, 0, 0, 0);
		update_dstrect = 0; // already done
	}
	if (update_dstrect){
		/*rect2(sdl->screen, &dstrect, 0x000000);          
		SDL_UpdateRect(sdl->screen, dstrect.x, dstrect.y, dstrect.w, dstrect.h);
        usleep(100000);
		rect2(sdl->screen, &dstrect, 0x800000);*/
/*        for (i=0; i<16; i++){
            SDL_Rect q;
            q.x = 100;
            q.y = 100+i*10;
            q.w = 20;
            q.h = 10;
            SDL_FillRect(sdl->screen, &q, sdl->termcol[i]);
          }
          */
        
		SDL_UpdateRect(sdl->screen, dstrect.x, dstrect.y, dstrect.w, dstrect.h);
	}
//    zsdl_test_bars(sdl->screen, 0);


#ifdef DEBUG_REDRAW
    if (startx.tv_sec!=0){
        struct timeval stopx;
        int sec, usec;
        gettimeofday(&stopx, NULL);
        usec=stopx.tv_usec-startx.tv_usec;
        sec=stopx.tv_sec-startx.tv_sec;
        if (usec<0){usec+=1000000;sec--;}
        dbg(" %d.%06d\n", sec, usec);
//        dbg("b %d.%06d %d.%06d\n", startx.tv_sec % 100, startx.tv_usec, stopx.tv_sec % 100, stopx.tv_usec);
    }
    gettimeofday(&startx, NULL);
#endif
}

SDL_Rect *new_rect(int x, int y, int w, int h){
    SDL_Rect *rect;

    rect = g_new0(SDL_Rect, 1);
    rect->x = x;
    rect->y = y;
    rect->w = w;
    rect->h = h;
    return rect;
}

void free_rect(SDL_Rect *rect){
    g_free(rect);
}



int sdl_get_terminal_size(int fd, int *x, int *y){
	*x=sdl->screen->w / zsdl->font_w;
	*y=sdl->screen->h / zsdl->font_h;
	if (gses && gses->osk && cfg->portrait && sdl->screen->w < sdl->screen->h){
		*y = (sdl->screen->h - gses->osk->surface->h) / zsdl->font_h;
	}
    return 0;
}

int handle_unicode(const SDL_Event *sev, struct event *ev){
    char iso[64];
    char *isoptr;
    char *uniptr;
    size_t in,out;
    int ret;

#ifdef Z_HAVE_SDL1
	char uni[2];
	if (!sev->key.keysym.unicode) return 0;
	uni[0] = sev->key.keysym.unicode & 0xff;
	uni[1] = sev->key.keysym.unicode >> 8;
	in = 2;
#endif
#ifdef Z_HAVE_SDL2
	char uni[64];
	strcpy(uni, sev->text.text);
	in = strlen(uni);
#endif
	out = sizeof(iso);

    isoptr=iso;
    uniptr=uni;
    
    /*dbg("uniptr=%p isoptr=%p in=%d out=%d\n", uniptr, isoptr, in, out);*/

	ret = iconv(sdl->iconvhandle, &uniptr, &in, &isoptr, &out);
    //err=errno;
    /*dbg("uniptr=%p isoptr=%p in=%d out=%d\n", uniptr, isoptr, in, out);*/
    
    if (debug_keyboard){
        //rintf(stderr, "Keyboard: unicode=0x%04x  iso:0x%02x '%c'\n", sev->key.keysym.unicode, (unsigned char)iso[0],isprint((unsigned char)iso[0])?(unsigned char)iso[0]:'.');
    }
    if (ret || !iso[0]){
		int err=errno;
		char *c = z_strdup_strerror(err);
#ifdef Z_HAVE_SDL1
        log_addf("Can't handle keystroke unicode=0x%04x  error=%s  iso=0x%02x\n", sev->key.keysym.unicode, c, (unsigned char)iso[0]);
#endif
#ifdef Z_HAVE_SDL2
		log_addf("Can't handle keystroke unicode='%s'  error=%s  iso=0x%02x\n", sev->text.text, c, (unsigned char)iso[0]);
#endif
		g_free(c);
		return 0;
    }

	//dbg("TEXTINPUT %c (%s)\n", (unsigned char)iso[0], dumpmods(NULL));

    ev->x=(unsigned char)iso[0];
    if (ev->x <= 26){    /* Ctrl+letter */
        ev->x +='@';
    }
	if (ev->x == 31) ev->x = '-';


#ifdef Z_HAVE_SDL1
	SDL_Keymod mod = sev->key.keysym.mod;
#endif
#ifdef Z_HAVE_SDL2
	SDL_Keymod mod = SDL_GetModState();
#endif
	if ((mod & (KMOD_LCTRL|KMOD_RALT)) != (KMOD_LCTRL|KMOD_RALT)){ // skip right alt which switches keyboard layout for accent
#if defined(Z_MSC_MINGW) && defined(Z_HAVE_SDL1)
		if (getaltstate()) ev->y|=KBD_ALT;
#else
		if (mod & (KMOD_ALT|KMOD_META)) ev->y|=KBD_ALT;
#endif
		if (mod & KMOD_CTRL)            ev->y|=KBD_CTRL;
	}

    return 1;
}

gpointer sdl_event_thread(gpointer handle){
    int fd;
    SDL_Event sev, sev2;
    struct event ev;
    int shift,k,sk,ret;
    struct timeval oldmtv, now;
    int oldmx = -1, oldmy = -1;
    double d;

    oldmtv.tv_sec = 0;
    oldmtv.tv_usec = 0;

    memset(&ev, 0, sizeof(struct event));
    
	fd = GPOINTER_TO_INT(handle);
    //dbg("SDL thread is alive\n");
	zg_thread_set_name("Tucnak SDL events");

/*	ret = SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE);
	sdl->screen = SDL_SetVideoMode(sdl->w, sdl->h, 0, SDL_SWSURFACE|SDL_RESIZABLE|SDL_HWPALETTE);
    sdl->done = 1;*/
    
    while(!sdl->event_thread_break){
        int oldaltsym = 0;
#if 0        
        SDL_KeyboardEvent *kev;
#endif                
        ev.ev = ev.x = ev.y = ev.b = 0;
        k=sk=0;
#ifndef EVENTS_IN_SAME_THREAD
		SDL_PumpEvents();
#endif


#ifdef Z_HAVE_SDL1
        int ne = SDL_PeepEvents(&sev, 1, SDL_GETEVENT, SDL_ALLEVENTS);
#endif
#ifdef Z_HAVE_SDL2
        int ne = SDL_PeepEvents(&sev, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
#endif
        if (ne < 0) {
            return GINT_TO_POINTER(-1);
        }
        if (ne == 0) {
            SDL_Delay(50);
            continue;
        }


#if 0
        switch (sev.type){
            case SDL_NOEVENT: dbg("SDL_NOEVENT\n"); break;
            case SDL_ACTIVEEVENT: dbg("SDL_ACTIVEEVENT\n"); break;
            case SDL_KEYDOWN: dbg("SDL_KEYDOWN\n"); break;
            case SDL_KEYUP: dbg("SDL_KEYUP\n"); break;
            case SDL_MOUSEMOTION: dbg("SDL_MOUSEMOTION\n"); break;
            case SDL_MOUSEBUTTONDOWN: dbg("SDL_MOUSEBUTTONDOWN\n"); break;
            case SDL_MOUSEBUTTONUP: dbg("SDL_MOUSEBUTTONUP\n"); break;
            case SDL_QUIT: dbg("SDL_QUIT\n"); break;
            case SDL_SYSWMEVENT: dbg("SDL_SYSWMEVENT\n"); break;
            case SDL_VIDEORESIZE: dbg("SDL_VIDEORESIZE\n"); break;
            case SDL_VIDEOEXPOSE: dbg("SDL_VIDEOEXPOSE\n"); break;
            default: dbg("SDL event type %d\n", sev.type); break;
        }
#endif

		
        char type[10];
        strcpy(type, "?");

		switch(sev.type){
#ifdef Z_HAVE_SDL2
		case SDL_TEXTINPUT:
			strcpy(type, "TEXTINPUT");
			SDL_Keymod mod = SDL_GetModState();
			//dbg("TEXTINPUT: '%s'%s\n", sev.text.text, (mod & KMOD_ALT) ? " +ALT" : "");
			if (mod & KMOD_ALT) break;
			ev.ev = EV_KBD;
			if (handle_unicode(&sev, &ev)) goto sendkeydown;

			dbg("to je divny, SDL_TEXTINPUT '%s' nebyla zpracovana\n", sev.text.text);
			break;
#endif
		case SDL_KEYDOWN:
			strcpy(type, "KEYDOWN");
#if 0
			//if (sev.key.keysym.sym != SDLK_LCTRL && sev.key.keysym.sym != SDLK_RCTRL && sev.key.keysym.sym != SDLK_LALT && sev.key.keysym.sym != SDLK_RALT && sev.key.keysym.sym != SDLK_LSHIFT && sev.key.keysym.sym != SDLK_RSHIFT)
			if (sev.key.keysym.scancode != SDL_SCANCODE_LCTRL)
				dbg("KEYDOWN: scan=%d sym=%d '%c' mod=%d (%s)   GetMods=(%s)\n", sev.key.keysym.scancode, sev.key.keysym.sym, sev.key.keysym.sym, sev.key.keysym.mod, dumpmods(&sev.key.keysym), dumpmods(NULL));
#endif                

			if (cfg->altsyms){
				oldaltsym = sdl->altsym;
				if (sev.key.keysym.sym == SDLK_LALT || sev.key.keysym.sym == SDLK_RALT){
					sdl->altsym = 1;
					//dbg("sdl->altsym=1\n");
				}else{
					sdl->altsym = 0;
					//dbg("sdl->altsym=0\n");
				}
			}

			ev.ev=EV_KBD;
			switch(sev.key.keysym.sym){

			case SDLK_BACKSPACE:    k= sk = KBD_BS;         goto handle;
			case SDLK_TAB:          k = sk = KBD_TAB;        goto handle;
			case SDLK_RETURN:
			case SDLK_KP_ENTER:     k = sk = KBD_ENTER;      goto handle;
			case SDLK_PAUSE:		/*fprintf(stderr, "PAUSE\n");*/      goto handle;
			case SDLK_ESCAPE:       k = sk = KBD_ESC;        goto handle;

			case SDLK_UP: 		    k = sk = KBD_UP;         goto handle;
			case SDLK_DOWN: 	    k = sk = KBD_DOWN;       goto handle;
			case SDLK_RIGHT: 	    k = sk = KBD_RIGHT;      goto handle;
			case SDLK_LEFT: 	    k = sk = KBD_LEFT;       goto handle;
			case SDLK_INSERT: 	    k = sk = KBD_INS;        goto handle;
			case SDLK_DELETE:       k = sk = KBD_DEL;        goto handle;
			case SDLK_HOME: 	    k = sk = KBD_HOME;       goto handle;
			case SDLK_END: 		    k = sk = KBD_END;        goto handle;
			case SDLK_PAGEUP: 	    k = sk = KBD_PGUP;       goto handle;
			case SDLK_PAGEDOWN:     k = sk = KBD_PGDN;       goto handle;
			case SDLK_F1: 		    k = sk = KBD_F1;         goto handle;
			case SDLK_F2: 		    k = sk = KBD_F2;         goto handle;
			case SDLK_F3: 		    k = sk = KBD_F3;         goto handle;
			case SDLK_F4: 		    k = sk = KBD_F4;         goto handle;
			case SDLK_F5: 		    k = sk = KBD_F5;         goto handle;
			case SDLK_F6: 		    k = sk = KBD_F6;         goto handle;
			case SDLK_F7: 		    k = sk = KBD_F7;         goto handle;
			case SDLK_F8: 		    k = sk = KBD_F8;         goto handle;
			case SDLK_F9: 		    k = sk = KBD_F9;         goto handle;
			case SDLK_F10: 		    k = sk = KBD_F10;        goto handle;
			case SDLK_F11: 		    k = sk = KBD_F11;        goto handle;
			case SDLK_F12: 		    k = sk = KBD_F12;        goto handle;
#ifdef Z_HAVE_SDL1
			case SDLK_KP0:          k=KBD_INS;   sk='0'; goto handle;
			case SDLK_KP1:          k=KBD_END;   sk='1'; goto handle;
			case SDLK_KP2:          k=KBD_DOWN;  sk='2'; goto handle;
			case SDLK_KP3:          k=KBD_PGDN;  sk='3'; goto handle;
			case SDLK_KP4:          k=KBD_LEFT;  sk='4'; goto handle;
			case SDLK_KP5:          k=KBD_DOWN;  sk='5'; goto handle;
			case SDLK_KP6:          k=KBD_RIGHT; sk='6'; goto handle;
			case SDLK_KP7:          k=KBD_HOME;  sk='7'; goto handle;
			case SDLK_KP8:          k=KBD_UP;    sk='8'; goto handle;
			case SDLK_KP9:          k=KBD_PGUP;  sk='9'; goto handle;
#endif
#ifdef Z_HAVE_SDL2 // special handled by TEXTINPUT, number here
			case SDLK_KP_0:          k = KBD_INS;   goto handle;
			case SDLK_KP_1:          k = KBD_END;   goto handle;
			case SDLK_KP_2:          k = KBD_DOWN;  goto handle;
			case SDLK_KP_3:          k = KBD_PGDN;  goto handle;
			case SDLK_KP_4:          k = KBD_LEFT;  goto handle;
			case SDLK_KP_5:          k = KBD_DOWN;  goto handle;
			case SDLK_KP_6:          k = KBD_RIGHT; goto handle;
			case SDLK_KP_7:          k = KBD_HOME;  goto handle;
			case SDLK_KP_8:          k = KBD_UP;    goto handle;
			case SDLK_KP_9:          k = KBD_PGUP;  goto handle;
#endif
#ifdef Z_HAVE_SDL1
			case SDLK_KP_PERIOD:     k = KBD_DEL;   sk='.'; goto handle; // jeste je SDLK_PERIOD
			case SDLK_KP_DIVIDE:     k = sk = '/';          goto handle;
			case SDLK_KP_MULTIPLY:   k = sk = '*';          goto handle;
			case SDLK_KP_MINUS: 	 k = sk = '-';          goto handle;
			case SDLK_KP_PLUS: 	     k = sk = '+';          goto handle;
#endif
			case SDLK_BACKQUOTE:    k = '`'; sk = '~';   goto handle;
			default: break;
			}
#ifdef Z_HAVE_SDL2
			if ((sev.key.keysym.mod & (KMOD_CTRL | KMOD_ALT)) != 0){
				switch (sev.key.keysym.scancode){
					case SDL_SCANCODE_KP_PERIOD:	k = KBD_DEL;   sk = '.'; goto handle;
					case SDL_SCANCODE_KP_DIVIDE:	k = sk = '/';            goto handle;
					case SDL_SCANCODE_KP_MULTIPLY:	k = sk = '*';            goto handle;
					case SDL_SCANCODE_KP_MINUS:		k = sk = '-';            goto handle;
					case SDL_SCANCODE_KP_PLUS:		k = sk = '+';            goto handle;
                    default: 
                        break;
				}
			}
			// else: without ctrl/alt keys are sent as TEXTINPUT
#endif

                int skip_unicode = 0;
                if (sev.key.keysym.sym >= SDLK_0 && sev.key.keysym.sym <= SDLK_9 
					&& sev.key.keysym.mod & KMOD_CTRL    // Ctrl+3 to Ctrl+9 send strange unicode values
					&& (sev.key.keysym.mod & KMOD_RALT) == 0) { // but right Alt sets LCTRL | RALT
						skip_unicode = 1; 
				}
#ifdef Z_HAVE_SDL1
                if (sev.key.keysym.unicode && !skip_unicode){
                    if (handle_unicode(&sev, &ev)) goto sendkeydown;
                    dbg("to je divny, unicode 0x%04x nebyla zpracovana\n", sev.key.keysym.unicode);
                }
#endif
#ifdef Z_HAVE_SDL2
				if ((sev.key.keysym.mod & (KMOD_CTRL | KMOD_ALT)) == 0){
					break;
				}
                skip_unicode = skip_unicode; // disable warning
#endif
				switch(sev.key.keysym.sym){
                    case SDLK_QUOTE:        k='\'';  sk='"';     break;
                    case SDLK_COMMA:        k=',';   sk='<';     break;
                    case SDLK_MINUS:        k='-';   sk='_';     break;
                    case SDLK_PERIOD:       k='.';   sk='>';     break;
                    case SDLK_SLASH:        k='/';   sk='?';     break;
                    case SDLK_0:            k='0';   sk=')';     break;                                            
                    case SDLK_1:            k='1';   sk='!';     break;                                            
                    case SDLK_2:            k='2';   sk='@';     break;                                            
                    case SDLK_3:            k='3';   sk='#';     break;                                            
                    case SDLK_4:            k='4';   sk='$';     break;                                            
                    case SDLK_5:            k='5';   sk='%';     break;                                            
                    case SDLK_6:            k='6';   sk='^';     break;                                            
                    case SDLK_7:            k='7';   sk='&';     break;                                            
                    case SDLK_8:            k='8';   sk='*';     break;                                            
                    case SDLK_9:            k='9';   sk='(';     break;                                            
                    case SDLK_SEMICOLON:    k=';';   sk=':';     break;
                    case SDLK_LESS:         k='<';   sk='>';     break;
                    case SDLK_EQUALS:       k='=';   sk='+';     break;
                    case SDLK_LEFTBRACKET:  k='[';   sk='{';     break;
                    case SDLK_BACKSLASH:    k='\\';  sk='|';     break;
                    case SDLK_RIGHTBRACKET: k=']';   sk='}';     break;
                    //case SDLK_BACKQUOTE:    k='`';   sk='~';     break;
                    case SDLK_AT:           k='@';   sk='2';     break;
                                                          
                    default: 
                        if (sev.key.keysym.sym<0x100){
                            k=sk=sev.key.keysym.sym;
                            if (k>='a' && k<='z'){
                                sk-='a'-'A';
                            }
                        }else{
                            k=sk=0;
                        }
                        break;
                }
			handle:;
                shift = (sev.key.keysym.mod & KMOD_SHIFT) != 0;
                if (k>='a' && k<='z') shift^=(sev.key.keysym.mod & KMOD_CAPS)!=0;
#ifdef Z_HAVE_SDL1
                if (sev.key.keysym.sym>=SDLK_KP0 && sev.key.keysym.sym<=SDLK_KP_PERIOD) shift^=(sev.key.keysym.mod & KMOD_NUM)!=0;
#endif
#ifdef Z_HAVE_SDL2
				if (sev.key.keysym.sym >= SDLK_KP_1 && sev.key.keysym.sym <= SDLK_KP_PERIOD)
					shift ^= (sev.key.keysym.mod & KMOD_NUM) != 0;
#endif
				

                if (shift){
					if (sk == 0) break; // handled by TEXTINPUT
                    ev.x=sk; 
                    if (sk>=256) ev.y|=KBD_SHIFT;
                }else{
                    ev.x=k;
                }

#if defined(Z_MSC_MINGW) && defined(Z_HAVE_SDL1)
				if (getaltstate()) ev.y|=KBD_ALT;
                if (sev.key.keysym.mod & (KMOD_LALT|KMOD_META)){
					if (!getaltstate()){
						int x = 0;
					}
				}
#else
                if (sev.key.keysym.mod & (KMOD_LALT|KMOD_META)){
                    ev.y|=KBD_ALT;
                }
#endif



                    
                if (sev.key.keysym.mod & KMOD_CTRL){
                    ev.y|=KBD_CTRL;
                }
                    
                
                if (ev.x) goto sendkeydown;
                break;
            case SDL_KEYUP:
				break;

            case SDL_MOUSEBUTTONDOWN:
                ev.ev=EV_MOUSE;
                ev.x=sev.button.x/FONT_W;
                ev.y=sev.button.y/FONT_H;
                ev.mx=sev.button.x;
                ev.my=sev.button.y;
				sdl->old_mouse_x = ev.x;
				sdl->old_mouse_y = ev.y;
                switch(sev.button.button){
                    case SDL_BUTTON_LEFT:      
                        sdl->mouse_drag=1;
                        sdl->drag_buttons=ev.b=B_LEFT;    
                        goto send;
                    case SDL_BUTTON_MIDDLE:    
                        sdl->mouse_drag=1;
                        sdl->drag_buttons=ev.b=B_MIDDLE;  
                        goto send;
                    case SDL_BUTTON_RIGHT:     
                        sdl->mouse_drag=1;
                        sdl->drag_buttons=ev.b=B_RIGHT;   
                        goto send;
#ifdef Z_HAVE_SDL1
                    case SDL_BUTTON_WHEELUP:   ev.b=B_WHUP;    goto send;
                    case SDL_BUTTON_WHEELDOWN: ev.b=B_WHDOWN;  goto send;
#endif
                }
                break;
#ifdef Z_HAVE_SDL2
			case SDL_MOUSEWHEEL:
				ev.ev = EV_MOUSE;
				ev.x = sdl->mouse_x / FONT_W;
				ev.y = sdl->mouse_y / FONT_H;
				ev.mx = sdl->mouse_x;
				ev.my = sdl->mouse_y;
				sdl->old_mouse_x = ev.x;
				sdl->old_mouse_y = ev.y;

				if (sev.wheel.y > 0){
					ev.b = B_WHUP;
					goto send;
				}
				if (sev.wheel.y < 0){
					ev.b = B_WHDOWN;
					goto send;
				}
				break;
#endif

            case SDL_MOUSEBUTTONUP:
                sdl->mouse_drag=0;
                ev.ev=EV_MOUSE;
                ev.x=sev.button.x/FONT_W;
                ev.y=sev.button.y/FONT_H;
                ev.mx=sev.button.x;
                ev.my=sev.button.y;
				ev.b = B_UP;
				if (ev.x == sdl->old_mouse_x && ev.y == sdl->old_mouse_y) ev.b |= B_CLICK;

                switch(sev.button.button){
                    case SDL_BUTTON_LEFT:      
						ev.b |= B_LEFT;
						goto send;
                    case SDL_BUTTON_MIDDLE:    
						ev.b |= B_MIDDLE;
						goto send;
                    case SDL_BUTTON_RIGHT:     
						ev.b |= B_RIGHT;
						goto send;
                }
                break;
            case SDL_MOUSEMOTION:
                ev.ev=EV_MOUSE;
                ev.x=sev.button.x/FONT_W;
                ev.y=sev.button.y/FONT_H;
                if (sdl->mouse_drag){ 
                    ev.b=sdl->drag_buttons|B_DRAG;
				}
                else
                    ev.b=B_MOVE;
                ev.mx=sev.button.x;
                ev.my=sev.button.y;
                sdl->mouse_x=sev.button.x;
                sdl->mouse_y=sev.button.y;
                while (1){
//					dbg("while &sev2=%p\n", &sev2);
#ifdef Z_HAVE_SDL1
					ret = SDL_PeepEvents(&sev2, 1, SDL_PEEKEVENT, SDL_ALLEVENTS);
#endif
#ifdef Z_HAVE_SDL2
					ret = SDL_PeepEvents(&sev2, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
#endif
					//			        dbg("PeepEvent2=%d %d\n", ret, sev2.type);
					if (ret != 1) break;
                    if (sev2.type!=SDL_MOUSEMOTION) break;
#ifdef Z_HAVE_SDL1
					ret = SDL_PeepEvents(&sev, 1, SDL_GETEVENT, SDL_ALLEVENTS);
#endif
#ifdef Z_HAVE_SDL2
					ret = SDL_PeepEvents(&sev, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
#endif
                    if (ret!=1) zinternal("Somebody stole my event");
 //   		        dbg("PeepEvent3 %d\n", sev.type);
                    ev.x=sev2.button.x/FONT_W;
                    ev.y=sev2.button.y/FONT_H;
                    ev.mx=sev2.button.x;
                    ev.my=sev2.button.y;
                    sdl->mouse_x=sev2.button.x;
                    sdl->mouse_y=sev2.button.y;
                }

                goto send_mouse;
                if (sdl->mouse_drag) goto send;
                continue;
                
#ifdef Z_HAVE_SDL1
            case SDL_VIDEORESIZE:
				ev.ev=EV_SDLRESIZE;
				ev.mx = sdl->resize_w = sev.resize.w;
				ev.my = sdl->resize_h = sev.resize.h;
                ev.x=ev.mx/FONT_W;
                ev.y=ev.my/FONT_H;
				dbg("SDL_VIDEORESIZE %d w=%d\n", sdl->resizeevents, sdl->resize_w);
				if (sdl->resizeevents > 0) continue;
				goto send;
#endif
#ifdef Z_HAVE_SDL2
			case SDL_WINDOWEVENT:
				if (sev.window.event == SDL_WINDOWEVENT_SIZE_CHANGED){
					ev.ev = EV_SDLRESIZE;
					ev.mx = sdl->resize_w = sev.window.data1;
					ev.my = sdl->resize_h = sev.window.data2;
					ev.x = ev.mx / FONT_W;
					ev.y = ev.my / FONT_H;
					//dbg("SDL_WINDOWEVENT_RESIZED %d w=%d\n", sdl->resizeevents, sdl->resize_w);
					if (sdl->resizeevents > 0) continue;
					goto send;
				}
				break;
#endif

			case SDL_QUIT:
                ev.ev=EV_KBD;
                ev.x='q';
                ev.y=KBD_ALT;
                goto send;
        }
        continue;
sendkeydown:        
        //dbg("cfg->altsyms=%d sdl->altsym=%d ev->x=%c\n", cfg->altsyms, sdl->altsym, ev.x);
        //dbg("SEND %s\n", type);
        if (oldaltsym){
            int oldk = ev.x;
            switch (ev.x){
                case 'q': ev.x = '1'; break;
                case 'w': ev.x = '2'; break;
                case 'e': ev.x = '3'; break;
                case 'r': ev.x = '4'; break;
                case 't': ev.x = '5'; break;
                case 'y': ev.x = '6'; break;
                case 'z': ev.x = '6'; break;
                case 'u': ev.x = '7'; break;
                case 'i': ev.x = '8'; break;
                case 'o': ev.x = '9'; break;
                case 'p': ev.x = '0'; break;
                case 'l': ev.x = '/'; break;
            }
            if (ev.x != oldk){
                dbg("altsym remap %c->%c\n", oldk, ev.x);
            }
        }
#ifdef Z_HAVE_SDL2
		//dbg("(%s) EV_KBD %d '%c' %s %s %s\n\n", sev.type == SDL_TEXTINPUT ? "TEXTINPUT" : (sev.type == SDL_KEYDOWN ? "KEYDOWN" : "?"), 
		//	ev.x, ev.x, ev.y & KBD_SHIFT ? "SHIFT" : "", ev.y & KBD_CTRL ? "CTRL" : "", ev.y & KBD_ALT ? "ALT" : "");
#endif

		goto send;
send_mouse:;
        gettimeofday(&now, NULL); 
        d = z_difftimeval_double(&now, &oldmtv);
        if (d > 0.0 && d < 1.0 && oldmx >= 0 && oldmy >= 0){
            ev.vx = (ev.mx - oldmx) / d; 
            ev.vy = (ev.my - oldmy) / d; 
        }else{
            ev.vx = 0.0;
            ev.vy = 0.0;
        }
        oldmx = ev.mx;
        oldmy = ev.my;
        oldmtv.tv_sec = now.tv_sec;
        oldmtv.tv_usec = now.tv_usec;

send:;        
//        dbg("ev=%ld x=%ld(%c) y=%ld b=%ld\n", ev.ev, ev.x, isprint(ev.x & 0xff)?ev.x:0, ev.y, ev.b);

        while(1){
            int len;
            MUTEX_LOCK(sdl->eventpipestate);
            len=sdl->eventpipestate;
            MUTEX_UNLOCK(sdl->eventpipestate);
            if (len<sizeof(ev)*5) break;
            //dbg("waiting for the sun %d\n", len);
            usleep(100000);
			if (sdl->event_thread_break) goto terminate;
        }
		ret = z_pipe_write(fd, &ev, sizeof(ev));

		//dbg("sdl_event_thread: ret=%d\n", fd);
        if (ret != sizeof(ev)) {
            int err;
            err=errno;
            if (err==32 && !sdl->event_thread_break){
                zinternal("Sem pridej konec vlakna, vole. Nakej nouma mi zavrel pajpu");
            }
            zinternal("can't write to event pipe");
            break;
        }   
        MUTEX_LOCK(sdl->eventpipestate);
        sdl->eventpipestate+=sizeof(ev);
		if (ev.ev == EV_SDLRESIZE) sdl->resizeevents++;
        /*dbg("w %d\n", sdl->eventpipestate);*/
        MUTEX_UNLOCK(sdl->eventpipestate);
        
        
    }
terminate:;
    dbg("sdl_thread exited %d\n", sdl->event_thread_break);
    
    return 0;
}


int sdl_attach_terminal(int in, int out, int ctl)
{
    /*struct terminal *term;*/
    /*dbg("attach_terminal\n");*/
    /*         -1  -1   -1          -1 */
	handle_trm(in, out, out, sdl->eventpipe[1], ctl); /* before sdl_thread! */
    
/*    dbg("after handle_trm %d\n", sdl->eventpipestate);*/
    /*sdl->pipefd=fd[1];
    sdl->event_thread_break=0;
    sdl->event_thread=g_tXXhread_create(sdl_event_thread, GINT_TO_POINTER(sdl->pipefd), TRUE, NULL);
    if (!sdl->event_thread) {
        error("ERROR: can't create event thread");
        return -1;
    } */
    
	if ((term = init_term(sdl->eventpipe[0], out, win_func)) != NULL) {
        handle_basic_signals(term); /* OK, this is race condition, but it must be so; GPM installs it's own buggy TSTP handler */
		return sdl->eventpipe[1];
    }

    z_pipe_close(sdl->eventpipe[0]);
    z_pipe_close(sdl->eventpipe[1]);
    return -1;
}



int sdl_set_title(gchar *title){
    
    if (!sdl) return 0;
    zg_free0(sdl->title);
    sdl->title=g_strdup(title);
#ifdef Z_HAVE_SDL1
    SDL_WM_SetCaption(sdl->title, NULL);
#endif
    return 0;
}

#ifdef Z_HAVE_LIBPNG
void sdl_screenshot(int topwindow){
    gchar *c; 
    int i;
    SDL_Surface *surface;

    if (!sdl) return;
    dbg("sdl_screenshot\n");

	if (gses->osk){
		struct zosk *bk = gses->osk;
		gses->osk = NULL;
		sdl_force_redraw();
		sdl_redraw_screen();
		gses->osk = bk;
	}

    if (topwindow){
        struct window *win;

        win=(struct window *)&(term->windows);
        dbg("win=%p prev=%p next=%p\n", win, win->prev, win->next);
        if (win->prev==win->next || !win->next->data){
            surface=sdl->screen;
        }else{
            SDL_Rect sr,dr;
            sr.x=sr.y=dr.w=dr.h=sr.w=sr.h=0;
            i=0;
            if (win->next->handler==menu_func || win->next->handler==mainmenu_func){
                    struct menu *menu;
                    menu=(struct menu *)win->next->data;
                    dr.x=-menu->x*FONT_W;
                    dr.y=-menu->y*FONT_H;
                    sr.w=menu->xw*FONT_W;
                    sr.h=menu->yw*FONT_H;
                    i++;
            }
            if (win->next->handler==dialog_func){
                    struct dialog_data *dd;
                    dd=(struct dialog_data *)win->next->data;
                    dr.x=-dd->x*FONT_W;
                    dr.y=-dd->y*FONT_H;
                    sr.w=dd->xw*FONT_W;
                    sr.h=dd->yw*FONT_H;
                    i++;
            }
            if (win->next->handler==cwwindow_func){
                    struct cwwin_data *cww;
                    cww=(struct cwwin_data *)win->next->data;
                    dr.x=-cww->x*FONT_W;
                    dr.y=-cww->y*FONT_H;
                    sr.w=cww->w*FONT_W;
                    sr.h=cww->h*FONT_H;
                    i++;
            }
            
            if (!i){
                surface=sdl->screen;
            }else{
                surface=SDL_CreateRGBSurface(SDL_SWSURFACE, sr.w, sr.h, sdl->bpp,                               
                        sdl->screen->format->Rmask, sdl->screen->format->Gmask, sdl->screen->format->Bmask, 0);
/*                dbg("sr=[%d,%d, %d,%d]\n", sr.x, sr.y, sr.w, sr.h);
                dbg("dr=[%d,%d, %d,%d]\n", dr.x, dr.y, dr.w, dr.h);  */
                SDL_BlitSurface(sdl->screen, NULL , surface, &dr);
/*                dbg("dr=[%d,%d, %d,%d]\n", dr.x, dr.y, dr.w, dr.h);
                SDL_BlitSurface(surface, NULL, sdl->screen, NULL);
                SDL_UpdateRect(sdl->screen, 0, 0, sdl->screen->w, sdl->screen->h);
                sleep(1);*/
            }
        }

    }else{
        surface=sdl->screen;
    }
        
    for (i=0;1;i++){  
        struct stat st;

        if (ctest)
            c=g_strdup_printf("%s/snap%c%d.png",ctest->directory, z_char_lc(aband->bandchar), i);
        else
            c=g_strdup_printf("%s/snap%d.png",tucnak_dir, i);
        if (stat(c, &st)!=0) break;
        g_free(c);
    }
    dbg("file=%s\n", c);
    
    if (zpng_save(surface, c, NULL)){
        log_addf(VTEXT(T_CANT_WRITE_S), c);
        errbox(VTEXT(T_CANT_WRITE), errno);
    }
    g_free(c);

    if (surface!=sdl->screen) SDL_FreeSurface(surface);
}
#endif

//extern char font_vga[4096];


#endif

void progress(char *m, ...){
#ifdef Z_HAVE_SDL
    va_list l;
	gchar *c, *d;
	int x, y, yy, w, h, tw;

	if (!sdl) return;
	if (!zsdl) return;
	if (!m) {
		if (term) memset(term->last_screen, 0, term->x * term->y * sizeof(unsigned));
		return;
	}				
    va_start(l, m);
    c = g_strdup_vprintf(m, l);
    dbg("%s\n", c);
	tw = strlen(c) + 6;
	w = tw * zsdl->font_w;
	w = sdl->progress_w = Z_MAX(w, sdl->progress_w);
	tw = w / zsdl->font_w;

	h = 3 * zsdl->font_h;
	x = (sdl->screen->w - w) / 2;
	y = (sdl->screen->h - h) / 2;
	yy = y;

	d = (char *)g_malloc(tw + 1);
	memset(d, ' ', tw);
	d[tw] = '\0';
	memset(d + 2, 0x94, Z_MIN(tw - 4, strlen(d)));
	d[1] = 0x86;
	d[tw - 2] = 0x8f;
	zsdl_printf(sdl->screen, x, yy, 0, sdl->gr[12], 0, "%s", d);
	yy += zsdl->font_h;

	memset(d, ' ', tw);
	d[1] = 0x83;
	memcpy(d + 3, c, strlen(c));
	d[tw - 2] = 0x83;
	zsdl_printf(sdl->screen, x, yy, 0, sdl->gr[12], 0, "%s", d);
	yy += zsdl->font_h;

	memset(d, ' ', tw);
	memset(d + 2, 0x94, tw - 4);
	d[1] = 0x90;
	d[tw - 2] = 0x85;
	zsdl_printf(sdl->screen, x, yy, 0, sdl->gr[12], 0, "%s", d);

	SDL_UpdateRect(sdl->screen, x, y, w, h);

	g_free(d);
	g_free(c);
    va_end(l);
#endif
}

void sdl_force_redraw(){
	if (!term) return;
	
	memset(term->last_screen, 0, term->x * term->y * sizeof(unsigned));
}

#ifdef Z_HAVE_SDL
void zsdl_test_bars(SDL_Surface *surface, int ofs){
    int i;
    int min = Z_MIN(surface->w, surface->h);
    for (i = 0; i < 16; i++){
        SDL_Rect r;
        r.x = min * (i + 1 + ofs) / 18;
        r.y = min * (i + 1) / 18;
        r.w = min / 18;
        r.h = min / 18;
        SDL_FillRect(surface, &r, sdl->termcol[i]);
    }
    SDL_UpdateRect(surface, 0, 0, 0, 0);
}

void tsdl_change_font(char how){
	if (!sdl) return;

	if (how == '+'){
#ifdef Z_HAVE_SDL_TTF
        if (zsdl->ttf != NULL) {
            if (cfg->fontheight >= 40) return;
            cfg->fontheight += 2;
        }
        else
#endif
        {
            if (cfg->fontheight == 32) return;
            if (cfg->fontheight < 16) cfg->fontheight = 16;
            else if (cfg->fontheight < 24) cfg->fontheight = 24;
            else cfg->fontheight = 32;
        }
	}
	if (how == '-'){
#ifdef Z_HAVE_SDL_TTF
        if (zsdl->ttf != NULL) {
            if (cfg->fontheight <= 12) return;
            cfg->fontheight -= 2;
        }
        else
#endif
        {
            if (cfg->fontheight == 13) return;
            if (cfg->fontheight > 24) cfg->fontheight = 24;
            else if (cfg->fontheight > 16) cfg->fontheight = 16;
            else cfg->fontheight = 13;
        }
	}
	sdl_setvideomode(sdl->screen->w, sdl->screen->h, 0);
	resize_terminal(NULL);
}

void tsdl_optimal_font(){
    if (!sdl) return;
	
	if (sdl->screen_w >= 1920 && sdl->screen_h >= 1080){
		cfg->fontheight = 32;
	} else if (sdl->screen_w >= 1680 && sdl->screen_h >= 1050){
		cfg->fontheight = 24;
	} else {
		return;
	}

	sdl_setvideomode(sdl->screen->w, sdl->screen->h, 0);
	resize_terminal(NULL);
}


#endif
