Beispiel #1
0
int draw( )
{
    // etape 2 : dessiner l'objet avec opengl

    // on commence par effacer la fenetre avant de dessiner quelquechose
    glClear(GL_COLOR_BUFFER_BIT);

    // on dessine le triangle avec une transformation : rotation autour de Z
    /*
        openGL utilise un repere droit, x vers la droite, y vers le haut et z devant (vers l'observateur)
        donc une rotation autour de l'axe Z permet de faire tourner le triangle dans l'image.
     */
     /*
    Transform T= make_rotationZ( 45 );
    draw(triangle, T, make_identity(), make_identity());

    return 1;   // on continue, renvoyer 0 pour sortir de l'application
    */

// on peut aussi controler la rotation du triangle au clavier...

    static float angle= 0;      // il faudrait declarer angle comme variable globale...
    if(key_state('j'))
        angle= angle + 1;
    if(key_state('k'))
        angle= angle - 1;

    Transform T= make_rotationZ( angle );
    draw(triangle, T, make_identity(), make_identity());

    return 1;

// utiliser une autre transformation : make_scale(), make_translation(), etc.
}
Beispiel #2
0
static int mod_state(struct vo *vo)
{
    int res = 0;
    if (key_state(vo, VK_CONTROL))
        res |= MP_KEY_MODIFIER_CTRL;
    if (key_state(vo, VK_SHIFT))
        res |= MP_KEY_MODIFIER_SHIFT;
    if (key_state(vo, VK_MENU))
        res |= MP_KEY_MODIFIER_ALT;
    return res;
}
Beispiel #3
0
void Keyboard::_update(double dt) {
    for(auto keysym: keys_down_) {
        for(auto conn: global_while_key_pressed_signals_) {
            conn.second(keysym.second, dt);
        }
    }

    for(auto p: key_while_down_signals_) {
        if(key_state(p.first)) {
            for(std::pair<InputConnection, KeyDownCallback> p2: p.second) {
                p2.second(keys_down_[p.first], dt);
            }
        }
    }
}
static void array_button_toggled_cb(GtkToggleButton *togglebutton, gpointer user_data) {
	RyosconfigLayerIlluminationDialog *dialog = RYOSCONFIG_LAYER_ILLUMINATION_DIALOG(user_data);
	RyosconfigLayerIlluminationDialogPrivate *priv = dialog->priv;
	guint8 value;
	guint8 *data;

	if (gtk_toggle_button_get_inconsistent(togglebutton)) {
		gtk_toggle_button_set_inconsistent(togglebutton, FALSE);
		gtk_toggle_button_set_active(togglebutton, TRUE);
	}

	value = key_state(dialog, gtk_toggle_button_get_active(togglebutton));
	data = g_object_get_data(G_OBJECT(togglebutton), array_key);
	ryos_custom_lights_set_from_array(priv->light_layer, data, value);

	ryosconfig_layer_send(dialog);
}
static gboolean key_press_event_cb(GtkWidget *dialog, GdkEventKey *event, gpointer user_data) {
	RyosconfigLayerIlluminationDialog *layer_illumination_dialog = RYOSCONFIG_LAYER_ILLUMINATION_DIALOG(dialog);
	RyosconfigLayerIlluminationDialogPrivate *priv = layer_illumination_dialog->priv;
	guint8 hid;
	guint8 index;
	gboolean on;

	// FIXME support remap

	hid = gaminggear_xkeycode_to_hid(event->hardware_keycode);
	index = ryos_hid_to_light_layer_index(hid);
	
	on = priv->light_layer->keys[index] != 0;
	priv->light_layer->keys[index] = key_state(layer_illumination_dialog, !on);

	ryosconfig_layer_send(layer_illumination_dialog);

	/* don't further process the event (disables return key to exit dialog) */
	return TRUE;
}
Beispiel #6
0
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
                                LPARAM lParam)
{
    if (message == WM_NCCREATE) {
        CREATESTRUCT *cs = (void*)lParam;
        SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)cs->lpCreateParams);
    }
    struct vo *vo = (void*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
    // message before WM_NCCREATE, pray to Raymond Chen that it's not important
    if (!vo)
        return DefWindowProcW(hWnd, message, wParam, lParam);
    struct vo_w32_state *w32 = vo->w32;

    switch (message) {
        case WM_ERASEBKGND: // no need to erase background seperately
            return 1;
        case WM_PAINT:
            w32->event_flags |= VO_EVENT_EXPOSE;
            break;
        case WM_MOVE: {
            POINT p = {0};
            ClientToScreen(w32->window, &p);
            w32->window_x = p.x;
            w32->window_y = p.y;
            mp_msg(MSGT_VO, MSGL_V, "[vo] move window: %d:%d\n",
                   w32->window_x, w32->window_y);
            break;
        }
        case WM_SIZE: {
            w32->event_flags |= VO_EVENT_RESIZE;
            RECT r;
            GetClientRect(w32->window, &r);
            vo->dwidth = r.right;
            vo->dheight = r.bottom;
            mp_msg(MSGT_VO, MSGL_V, "[vo] resize window: %d:%d\n",
                   vo->dwidth, vo->dheight);
            break;
        }
        case WM_SIZING:
            if (vo->opts->keepaspect && !vo->opts->fs && vo->opts->WinID < 0) {
                RECT *rc = (RECT*)lParam;
                // get client area of the windows if it had the rect rc
                // (subtracting the window borders)
                RECT r = *rc;
                subtract_window_borders(w32->window, &r);
                int c_w = r.right - r.left, c_h = r.bottom - r.top;
                float aspect = vo->aspdat.asp;
                int d_w = c_h * aspect - c_w;
                int d_h = c_w / aspect - c_h;
                int d_corners[4] = { d_w, d_h, -d_w, -d_h };
                int corners[4] = { rc->left, rc->top, rc->right, rc->bottom };
                int corner = get_resize_border(wParam);
                if (corner >= 0)
                    corners[corner] -= d_corners[corner];
                *rc = (RECT) { corners[0], corners[1], corners[2], corners[3] };
                return TRUE;
            }
            break;
        case WM_CLOSE:
            mplayer_put_key(vo->key_fifo, MP_KEY_CLOSE_WIN);
            break;
        case WM_SYSCOMMAND:
            switch (wParam) {
                case SC_SCREENSAVE:
                case SC_MONITORPOWER:
                    mp_msg(MSGT_VO, MSGL_V, "vo: win32: killing screensaver\n");
                    return 0;
            }
            break;
        case WM_KEYDOWN:
        case WM_SYSKEYDOWN: {
            int mpkey = lookup_keymap_table(vk_map, wParam);
            if (mpkey)
                mplayer_put_key(vo->key_fifo, mpkey | mod_state(vo));
            if (wParam == VK_F10)
                return 0;
            break;
        }
        case WM_CHAR:
        case WM_SYSCHAR: {
            int mods = mod_state(vo);
            int code = wParam;
            // Windows enables Ctrl+Alt when AltGr (VK_RMENU) is pressed.
            // E.g. AltGr+9 on a German keyboard would yield Ctrl+Alt+[
            // Warning: wine handles this differently. Don't test this on wine!
            if (key_state(vo, VK_RMENU))
                mods &= ~(MP_KEY_MODIFIER_CTRL | MP_KEY_MODIFIER_ALT);
            // Apparently Ctrl+A to Ctrl+Z is special cased, and produces
            // character codes from 1-26. Work it around.
            // Also, enter/return (including the keypad variant) and CTRL+J both
            // map to wParam==10. As a workaround, check VK_RETURN to
            // distinguish these two key combinations.
            if ((mods & MP_KEY_MODIFIER_CTRL) && code >= 1 && code <= 26
                && !key_state(vo, VK_RETURN))
                code = code - 1 + (mods & MP_KEY_MODIFIER_SHIFT ? 'A' : 'a');
            if (code >= 32 && code < (1<<21)) {
                mplayer_put_key(vo->key_fifo, code | mods);
                // At least with Alt+char, not calling DefWindowProcW stops
                // Windows from emitting a beep.
                return 0;
            }
            break;
        }
        case WM_LBUTTONDOWN:
            if (!vo->opts->nomouse_input && (vo->opts->fs || (wParam & MK_CONTROL)))
            {
                mplayer_put_key(vo->key_fifo, MP_MOUSE_BTN0 | mod_state(vo));
                break;
            }
            if (!vo->opts->fs) {
                ReleaseCapture();
                SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
                return 0;
            }
            break;
        case WM_MBUTTONDOWN:
            if (!vo->opts->nomouse_input)
                mplayer_put_key(vo->key_fifo, MP_MOUSE_BTN1 | mod_state(vo));
            break;
        case WM_RBUTTONDOWN:
            if (!vo->opts->nomouse_input)
                mplayer_put_key(vo->key_fifo, MP_MOUSE_BTN2 | mod_state(vo));
            break;
        case WM_MOUSEMOVE:
            vo_mouse_movement(vo, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
            break;
        case WM_MOUSEWHEEL:
            if (!vo->opts->nomouse_input) {
                int x = GET_WHEEL_DELTA_WPARAM(wParam);
                if (x > 0)
                    mplayer_put_key(vo->key_fifo, MP_MOUSE_BTN3 | mod_state(vo));
                else
                    mplayer_put_key(vo->key_fifo, MP_MOUSE_BTN4 | mod_state(vo));
            }
            break;
        case WM_XBUTTONDOWN:
            if (!vo->opts->nomouse_input) {
                int x = HIWORD(wParam);
                if (x == 1)
                    mplayer_put_key(vo->key_fifo, MP_MOUSE_BTN5 | mod_state(vo));
                else // if (x == 2)
                    mplayer_put_key(vo->key_fifo, MP_MOUSE_BTN6 | mod_state(vo));
            }
            break;
    }

    return DefWindowProcW(hWnd, message, wParam, lParam);
}
Beispiel #7
0
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
                                LPARAM lParam)
{
    if (message == WM_NCCREATE) {
        CREATESTRUCT *cs = (void*)lParam;
        SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)cs->lpCreateParams);
    }
    struct vo *vo = (void*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
    // message before WM_NCCREATE, pray to Raymond Chen that it's not important
    if (!vo)
        return DefWindowProcW(hWnd, message, wParam, lParam);
    struct vo_w32_state *w32 = vo->w32;
    int mouse_button = 0;

    switch (message) {
        case WM_ERASEBKGND: // no need to erase background seperately
            return 1;
        case WM_PAINT:
            w32->event_flags |= VO_EVENT_EXPOSE;
            break;
        case WM_MOVE: {
            POINT p = {0};
            ClientToScreen(w32->window, &p);
            w32->window_x = p.x;
            w32->window_y = p.y;
            MP_VERBOSE(vo, "move window: %d:%d\n",
                   w32->window_x, w32->window_y);
            break;
        }
        case WM_SIZE: {
            w32->event_flags |= VO_EVENT_RESIZE;
            RECT r;
            GetClientRect(w32->window, &r);
            vo->dwidth = r.right;
            vo->dheight = r.bottom;
            MP_VERBOSE(vo, "resize window: %d:%d\n",
                   vo->dwidth, vo->dheight);
            break;
        }
        case WM_SIZING:
            if (vo->opts->keepaspect && !vo->opts->fullscreen &&
                vo->opts->WinID < 0)
            {
                RECT *rc = (RECT*)lParam;
                // get client area of the windows if it had the rect rc
                // (subtracting the window borders)
                RECT r = *rc;
                subtract_window_borders(w32->window, &r);
                int c_w = r.right - r.left, c_h = r.bottom - r.top;
                float aspect = w32->o_dwidth / (float) MPMAX(w32->o_dheight, 1);
                int d_w = c_h * aspect - c_w;
                int d_h = c_w / aspect - c_h;
                int d_corners[4] = { d_w, d_h, -d_w, -d_h };
                int corners[4] = { rc->left, rc->top, rc->right, rc->bottom };
                int corner = get_resize_border(wParam);
                if (corner >= 0)
                    corners[corner] -= d_corners[corner];
                *rc = (RECT) { corners[0], corners[1], corners[2], corners[3] };
                return TRUE;
            }
            break;
        case WM_CLOSE:
            mp_input_put_key(vo->input_ctx, MP_KEY_CLOSE_WIN);
            break;
        case WM_SYSCOMMAND:
            switch (wParam) {
            case SC_SCREENSAVE:
            case SC_MONITORPOWER:
                if (w32->disable_screensaver) {
                    MP_VERBOSE(vo, "win32: killing screensaver\n");
                    return 0;
                }
                break;
            }
            break;
        case WM_KEYDOWN:
        case WM_SYSKEYDOWN: {
            int mpkey = lookup_keymap_table(vk_map, wParam);
            if (mpkey)
                mp_input_put_key(vo->input_ctx, mpkey | mod_state(vo));
            if (wParam == VK_F10)
                return 0;
            break;
        }
        case WM_CHAR:
        case WM_SYSCHAR: {
            int mods = mod_state(vo);
            int code = wParam;
            // Windows enables Ctrl+Alt when AltGr (VK_RMENU) is pressed.
            // E.g. AltGr+9 on a German keyboard would yield Ctrl+Alt+[
            // Warning: wine handles this differently. Don't test this on wine!
            if (key_state(vo, VK_RMENU) && mp_input_use_alt_gr(vo->input_ctx))
                mods &= ~(MP_KEY_MODIFIER_CTRL | MP_KEY_MODIFIER_ALT);
            // Apparently Ctrl+A to Ctrl+Z is special cased, and produces
            // character codes from 1-26. Work it around.
            // Also, enter/return (including the keypad variant) and CTRL+J both
            // map to wParam==10. As a workaround, check VK_RETURN to
            // distinguish these two key combinations.
            if ((mods & MP_KEY_MODIFIER_CTRL) && code >= 1 && code <= 26
                && !key_state(vo, VK_RETURN))
                code = code - 1 + (mods & MP_KEY_MODIFIER_SHIFT ? 'A' : 'a');
            if (code >= 32 && code < (1<<21)) {
                mp_input_put_key(vo->input_ctx, code | mods);
                // At least with Alt+char, not calling DefWindowProcW stops
                // Windows from emitting a beep.
                return 0;
            }
            break;
        }
        case WM_SETCURSOR:
            if (LOWORD(lParam) == HTCLIENT && !w32->cursor_visible) {
                SetCursor(NULL);
                return TRUE;
            }
            break;
        case WM_MOUSELEAVE:
            w32->tracking = FALSE;
            mp_input_put_key(vo->input_ctx, MP_KEY_MOUSE_LEAVE);
            break;
        case WM_MOUSEMOVE: {
            if (!w32->tracking)
                w32->tracking = TrackMouseEvent(&w32->trackEvent);
            // Windows can send spurious mouse events, which would make the mpv
            // core unhide the mouse cursor on completely unrelated events. See:
            //  https://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
            int x = GET_X_LPARAM(lParam);
            int y = GET_Y_LPARAM(lParam);
            if (x != w32->mouse_x || y != w32->mouse_y) {
                w32->mouse_x = x;
                w32->mouse_y = y;
                vo_mouse_movement(vo, x, y);
            }
            break;
        }
        case WM_LBUTTONDOWN:
            mouse_button = MP_MOUSE_BTN0 | MP_KEY_STATE_DOWN;
            break;
        case WM_LBUTTONUP:
            mouse_button = MP_MOUSE_BTN0 | MP_KEY_STATE_UP;
            break;
        case WM_MBUTTONDOWN:
            mouse_button = MP_MOUSE_BTN1 | MP_KEY_STATE_DOWN;
            break;
        case WM_MBUTTONUP:
            mouse_button = MP_MOUSE_BTN1 | MP_KEY_STATE_UP;
            break;
        case WM_RBUTTONDOWN:
            mouse_button = MP_MOUSE_BTN2 | MP_KEY_STATE_DOWN;
            break;
        case WM_RBUTTONUP:
            mouse_button = MP_MOUSE_BTN2 | MP_KEY_STATE_UP;
            break;
        case WM_MOUSEWHEEL: {
            int x = GET_WHEEL_DELTA_WPARAM(wParam);
            mouse_button = x > 0 ? MP_MOUSE_BTN3 : MP_MOUSE_BTN4;
            break;
        }
        case WM_XBUTTONDOWN:
            mouse_button = HIWORD(wParam) == 1 ? MP_MOUSE_BTN5 : MP_MOUSE_BTN6;
            mouse_button |= MP_KEY_STATE_DOWN;
            break;
        case WM_XBUTTONUP:
            mouse_button = HIWORD(wParam) == 1 ? MP_MOUSE_BTN5 : MP_MOUSE_BTN6;
            mouse_button |= MP_KEY_STATE_UP;
            break;
    }

    if (mouse_button && vo->opts->enable_mouse_movements) {
        int x = GET_X_LPARAM(lParam);
        int y = GET_Y_LPARAM(lParam);
        mouse_button |= mod_state(vo);
        if (mouse_button == (MP_MOUSE_BTN0 | MP_KEY_STATE_DOWN) &&
            !vo->opts->fullscreen && !mp_input_test_dragging(vo->input_ctx, x, y))
        {
            // Window dragging hack
            ReleaseCapture();
            SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
            return 0;
        }
        mp_input_put_key(vo->input_ctx, mouse_button);
    }

    return DefWindowProcW(hWnd, message, wParam, lParam);
}
Beispiel #8
0
int move_player1(ENTITY *e,int frame_time)
{
	int printinfo=FALSE;
	if(e==0)
		return 0;
	if(key_state('p')){
		switch(e->state_action){
		default:
			e->state_action=S_ACTION_PAUSE1;
			break;
		case S_ACTION_PAUSE3:
		case S_ACTION_PAUSE2:
			e->state_action=S_ACTION_PAUSE3;
			break;
		}
	}else{
		switch(e->state_action){
		default:
			e->state_action=0;
			break;
		case S_ACTION_PAUSE2:
		case S_ACTION_PAUSE1:
			e->state_action=S_ACTION_PAUSE2;
			break;
		}
	}
	if(e->state_action==S_ACTION_PAUSE1 || e->state_action==S_ACTION_PAUSE2)
		return 0;
	if(key_state(VK_LEFT)){
		//printf("delta=%i\n",delta);
		e->rotx=-1;
		e->speedx=frame_time/2;
	}
	else if(key_state(VK_RIGHT)){
		e->rotx=1;
		e->speedx=frame_time/2;
	}
	else{
		e->speedx-=frame_time;
	}
	if(key_state(VK_DOWN)){
		e->posz+=100;
		printinfo=TRUE;
	}
	if(key_state(VK_UP)){
		e->posz-=100;
		printinfo=TRUE;
	}

	if(key_state(VK_CONTROL)){
		e->speedy=frame_time;
	}
	else{
		e->speedy-=frame_time/6;
	}
	if(key_state(VK_MENU)){
		int s[3]={0,0,0},p[3]={0,0,0};
		if(e->rotx<0)
			s[0]=-2;
		else
			s[0]=2;
		p[0]=e->posx;
		p[1]=e->posy;
		p[2]=e->posz;
		if(e->state_action==0){
			e->state_action=S_ACTION_FIRE;
			e->stime_action=0;
			add_bullet(e,s,p);
		}
		printf("menu\n");
	}
	switch(e->state_action){
	case S_ACTION_FIRE:
		e->stime_action+=frame_time;
		if(e->stime_action>250)
			e->state_action=0;
		break;
	}

	if(e->speedx>100)
		e->speedx=100;
	else if(e->speedx<0)
		e->speedx=0;

	if(e->speedy>100)
		e->speedy=100;
	else if(e->speedy<-100)
		e->speedy=-100;
	
	e->posx+=e->speedx*e->rotx;

	e->posy+=e->speedy;

	//if(posz>100)
	//	posz=0;
	//else if(posz<0)
	//	posz=0;

	if(e->posx>500)
		e->posx=500;
	else if(e->posx<-500)
		e->posx=-500;

	if(e->posy>500)
		e->posy=500;
	else if(e->posy<-100){
		e->posy=-100;
		e->speedy=0;
	}

	if(e->speedx==0)
		e->frame=0;
	else{
		e->time+=e->speedx;
		if(e->time>12){
			e->time=0;
			e->frame++;
			if(e->frame>=(e->tcols*e->trows))
				e->frame=0;
		}
	}
//	if(e->speedx!=0 || e->speedy!=0 || e->speedz!=0 || printinfo)
//		printf("sx=% 5.1f sy=% 5.1f sz=% 5.1f px=% 5.1f py=% 5.1f pz=% 5.1f\n",e->speedx,e->speedy,e->speedz,e->posx,e->posy,e->posz);
	return 0;
}
int draw( void )
{
    
    if(wireframe)
    {
        glClearColor(1, 1, 1, 1);
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        glLineWidth(2);
    }
    else
    {
        glClearColor(0.2f, 0.2f, 0.2f, 1);
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }
    
    // effacer l'image
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    if(key_state('r'))
    {
        clear_key_state('r');
        reload_program();
    }
    
    // recupere les mouvements de la souris
    int mx, my;
    unsigned int mb= SDL_GetRelativeMouseState(&mx, &my);
    int mousex, mousey;
    SDL_GetMouseState(&mousex, &mousey);
    
    // deplace la camera
    if(mb & SDL_BUTTON(1))
        orbiter_rotation(camera, mx, my);      // tourne autour de l'objet
    else if(mb & SDL_BUTTON(2))
        orbiter_translation(camera, (float) mx / (float) window_width(), (float) my / (float) window_height()); // deplace le point de rotation
    else if(mb & SDL_BUTTON(3))
        orbiter_move(camera, mx);           // approche / eloigne l'objet
    
    // recupere les transformations
    Transform model= make_identity();
    Transform view= orbiter_view_transform(camera);
    Transform projection= orbiter_projection_transform(camera, window_width(), window_height(), 45);
    Transform viewport= make_viewport(window_width(), window_height());
    
    Transform mvp= projection * view * model;
    Transform mvpInv= make_inverse(mvp);
    Transform mv= model * view;
    
    // affiche l'objet
    if(program_failed == false)
    {
        if(key_state('w'))
        {
            clear_key_state('w');
            wireframe= !wireframe;
        }
        
        // configuration minimale du pipeline
        glBindVertexArray(vao);
        glUseProgram(program);
        
        // affecte une valeur aux uniforms
        // transformations standards
        program_uniform(program, "modelMatrix", model);
        program_uniform(program, "modelInvMatrix", make_inverse(model));
        program_uniform(program, "viewMatrix", view);
        program_uniform(program, "viewInvMatrix", make_inverse(view));
        program_uniform(program, "projectionMatrix", projection);
        program_uniform(program, "projectionInvMatrix", make_inverse(projection));
        program_uniform(program, "viewportMatrix", viewport);
        program_uniform(program, "viewportInvMatrix", make_inverse(viewport));
        
        program_uniform(program, "mvpMatrix", mvp);
        program_uniform(program, "mvpInvMatrix", mvpInv);
        
        program_uniform(program, "mvMatrix", mv);
        program_uniform(program, "normalMatrix", make_normal_transform(mv));
        
        // interactions
        program_uniform(program, "viewport", make_vec2(window_width(), window_height()));
        program_uniform(program, "time", (float) SDL_GetTicks());
        program_uniform(program, "motion", make_vec3(mx, my, mb & SDL_BUTTON(1)));
        program_uniform(program, "mouse", make_vec3(mousex, mousey, mb & SDL_BUTTON(1)));
        
        // textures
        for(unsigned int i= 0; i < (unsigned int) textures.size(); i++)
        {
            char uniform[1024];
            sprintf(uniform, "texture%d", i);
            program_use_texture(program, uniform, i, textures[i]);
        }
        
        // go
        glDrawArrays(GL_TRIANGLES, 0, vertex_count);
    }
    
    // affiche les infos
    begin(widgets);
    if(program_failed)
    {
        label(widgets, "[error] program '%s'", program_filename.path);
        begin_line(widgets);
        text_area(widgets, 20, program_log.c_str(), program_area);
    }
    else
    {
        label(widgets, "program '%s' running...", program_filename.path);
        if(mesh_filename[0] != 0)
        {
            begin_line(widgets);
            label(widgets, "mesh '%s', %u positions, %u texcoords, %u normals", mesh_filename.path, 
                (unsigned int) mesh.positions.size(),
                (unsigned int) mesh.texcoords.size(),
                (unsigned int) mesh.normals.size());
        }
        for(unsigned int i= 0; i < (unsigned int) texture_filenames.size(); i++)
        {
            begin_line(widgets);
            label(widgets, "texture%u '%s'", i, texture_filenames[i].path);
        }
    }
    end(widgets);
    
    draw(widgets, window_width(), window_height());
    
    
    if(key_state('s'))
    {
        clear_key_state('s');
        screenshot("shader_kit.png");
    }
    
    if(key_state('c'))
    {
        clear_key_state('c');
        write_orbiter(camera, "orbiter.txt");
    }
    if(key_state('v'))
    {
        clear_key_state('v');
        camera= read_orbiter("orbiter.txt");
    }
    
    return 1;
}