Multiple Windows dan Event Windows

Pada SDL versi 1.2, program yang dibuat dengan SDL hanya bisa menampilkan satu window saja. Ini berbeda dengan SDL 2.0 yang menambahkan banyak fitur baru. Salah satu fitur tersebut adalah satu program yang bisa menggunakan beberapa window.

Untuk membuat window kita bisa menggunakan fungsi SDL_CreateWindow. Selain itu, kita perlu variabel bertipe SDL_Window untuk menyimpan informasi yang dikembalikan oleh fungsi SDL_CreateWindow. 

Setelah window berhasil dibuat, kita bisa menggunakan SDL_GetWindowSurface untuk mendapatkan surface dari tiap window dalam program. Jika kita tidak menggunakan surface, kita bisa juga membuat renderer untuk setiap window dengan menggunakan function SDL_CreateRenderer.

Kali ini, kita akan menggunakan SDL_Renderer. Kita akan membuat fungsi khusus yang berisi pembuatan windows dan renderernya. Karena setiap window memiliki informasinya masing-masing, sebaiknya kita membuat struct yang menyimpan informasi-informasi terkait dengan window-window tersebut. Header, struct, dan variabel lain yang akan kita perlukan dalam program bisa kalian lihat di bawah ini.

#include <SDL2/SDL.h>
#define TOTAL_WINDOWS 3
#define salah 0
#define benar 1

struct info_window{
    SDL_Window *mwindow;
    SDL_Renderer *mrenderer;
    int idwindow;
    int tinggi;
    int lebar;
    int tutup;
    int mKeyboardfocus;
    int mMinimized;
    int mMousefocus;
}windowku[TOTAL_WINDOWS];

Karena kita akan membuat beberapa window, kita juga perlu menyediakan function untuk membuat window supaya program menjadi lebih singkat.

int buat_window(const char *nama, info_window *twindow){
    twindow->mwindow=SDL_CreateWindow(nama, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
    if( twindow->mwindow == NULL ){
        printf( "Error : %s", SDL_GetError() );
        return 0;
    }else{
    twindow->mrenderer = SDL_CreateRenderer(twindow->mwindow, -1, SDL_RENDERER_ACCELERATED);
    if(twindow->mrenderer!=NULL){
        SDL_SetRenderDrawColor(twindow->mrenderer, 0xFF, 0xFF, 0xFF, 0xFF);
        SDL_GetWindowID(twindow->mwindow);
        }else return 0;
    }
    return 1; 
}

Selain function untuk membuat window, kita juga perlu function untuk menghandle event dari setiap window. Event khusus untuk window diperlakukan berbeda dengan jenis event lainnya. Event untuk window (seperti saat window ditutup atau saat window mendapatkan fokus) memerlukan penanganan khusus karena event window memberikan nilai yang sama pada "type" yang merupakan anggota dari SDL_Event. Jenis event untuk windows lebih spesifiknya disimpan dalam "window.event". Selain itu kita bisa mengetahui window yang mendapatkan event dari nilai "window.windowID". Beberapa event khusus untuk window dalam SDL bisa kalian lihat di bawah ini.

  1. SDL_WINDOWEVENT_SHOWN
  2. SDL_WINDOWEVENT_HIDDEN
  3. SDL_WINDOWEVENT_EXPOSED
  4. SDL_WINDOWEVENT_MOVED
  5. SDL_WINDOWEVENT_RESIZED
  6. SDL_WINDOWEVENT_SIZE_CHANGED
  7. SDL_WINDOWEVENT_MINIMIZED
  8. SDL_WINDOWEVENT_MAXIMIZED
  9. SDL_WINDOWEVENT_RESTORED
  10. SDL_WINDOWEVENT_ENTER
  11. SDL_WINDOWEVENT_LEAVE
  12. SDL_WINDOWEVENT_FOCUS_GAINED
  13. SDL_WINDOWEVENT_FOCUS_LOST
  14. SDL_WINDOWEVENT_CLOSE
  15. SDL_WINDOWEVENT_TAKE_FOCUS
  16. SDL_WINDOWEVENT_HIT_TEST

Berikut ini adalah contoh function untuk menghandle event khusus window saat "type" bernilai SDL_WINDOWEVENT.

int event_window(SDL_Event *e, info_window *twindow){    
    if( e->window.windowID == twindow->idwindow ){
        switch( e->window.event ){
	    //window terlihat
            case SDL_WINDOWEVENT_SHOWN:
	        twindow->tutup=salah;
                break;

            //Window menghilang
            case SDL_WINDOWEVENT_HIDDEN:
	        twindow->tutup=benar;
                break;

            //Window berubah ukuran
            case SDL_WINDOWEVENT_SIZE_CHANGED:
                twindow->lebar = e->window.data1;
                twindow->tinggi = e->window.data2;
                SDL_RenderPresent( twindow->mrenderer );
                break;

            case SDL_WINDOWEVENT_EXPOSED:
                SDL_RenderPresent( twindow->mrenderer );
                break;

            //Mouse enter
            case SDL_WINDOWEVENT_ENTER:
	        twindow->mMousefocus=benar;
                break;
            
            //Mouse exit
            case SDL_WINDOWEVENT_LEAVE:
	        twindow->mMousefocus=salah;
                break;

            //Keyboard focus gained
            case SDL_WINDOWEVENT_FOCUS_GAINED:
                twindow->mKeyboardfocus=benar;
                break;
            
            //Keyboard focus lost
            case SDL_WINDOWEVENT_FOCUS_LOST:
	        twindow->mKeyboardfocus=salah;
                break;

            //Window minimized
            case SDL_WINDOWEVENT_MINIMIZED:
	        twindow->mMinimized=benar;
                break;

            //Window maximized
            case SDL_WINDOWEVENT_MAXIMIZED:
	        twindow->mMinimized=salah;
                break;
            
            //Window restored
            case SDL_WINDOWEVENT_RESTORED:
	        twindow->mMinimized=salah;
                break;
            //Window ditutup
            case SDL_WINDOWEVENT_CLOSE:
    	        SDL_HideWindow( twindow->mwindow);
	        break;
	    }
    }
    return 0;
}

Fungsi utama dari program berisi beberapa loop karena kita menggunakan beberapa window. Loop yang pertama adalah loop untuk membuat window baru yang jumlahnya sesuai dengan nilai TOTAL_WINDOWS. Jika windows berhasil dibuat, maka loop berikutnya adalah loop untuk menghandle event yang akan dieksekusi hingga semua window ditutup.

int main(int argc, char* args[]){
SDL_Event evt;
int hasil;
    if( SDL_Init( SDL_INIT_VIDEO ) < 0){
        printf( "Error: %s\n", SDL_GetError() );
    }else{
	for(int i=0;i<TOTAL_WINDOWS;i++){
	    hasil+=buat_window("SDL_test", &windowku[i]);
	}
	
        if(hasil>=TOTAL_WINDOWS){
	    int berhenti=salah;
            while(berhenti==salah){
		while(SDL_PollEvent(&evt)){
		    switch(evt.type){
			case SDL_WINDOWEVENT :
			    for( int i = 0; i < TOTAL_WINDOWS; i++ ){
			        event_window(&evt, &windowku[i]);
			    }
			break;
			case SDL_KEYDOWN:
			    switch(evt.key.keysym.sym){
			        case SDLK_1:
		    		    SDL_ShowWindow(windowku[0].mwindow);
       				    SDL_RaiseWindow(windowku[0].mwindow);
				    break;
                                case SDLK_2:
       				    SDL_ShowWindow(windowku[1].mwindow);
			            SDL_RaiseWindow(windowku[1].mwindow);
				    break;
			        case SDLK_3:
			            SDL_ShowWindow(windowku[2].mwindow);
                                    SDL_RaiseWindow(windowku[2].mwindow);
			            break;
			    }
		    }
		}

		for(int i=0;i<TOTAL_WINDOWS;i++){
		    SDL_RenderClear(windowku[i].mrenderer);
		    SDL_RenderPresent(windowku[i].mrenderer);
		}
		
		int semua_tutup=benar;
		for(int i=0;i<TOTAL_WINDOWS;i++){
		    if(windowku[i].tutup==salah){
			semua_tutup=salah;
			break;
		    }
		}
		if(semua_tutup==benar)berhenti=benar;
	    }
	}
    }
    for(int i=0;i<3;i++)SDL_DestroyWindow( windowku[i].mwindow );
    SDL_Quit();

    return 0;
}

Pada saat program baru dijalankan, program seharusnya akan menampilkan beberapa window sesuai nilai yang diberikan pada TOTAL_WINDOWS. Program akan berhenti saat semua window "ditutup". Setelah semua window ditutup, kita perlu menggunakan SDL_DestroyWindow untuk membersihkan memori yang digunakan setiap window karena window belum benar-benar ditutup setelah window disembunyikan.

Program utuhnya bisa kalian lihat di bawah ini.

#include <SDL2/SDL.h>
#define TOTAL_WINDOWS 3
#define salah 0
#define benar 1

struct info_window{
    SDL_Window *mwindow;
    SDL_Renderer *mrenderer;
    int idwindow;
    int tinggi;
    int lebar;
    int tutup;
    int mKeyboardfocus;
    int mMinimized;
    int mMousefocus;
}windowku[TOTAL_WINDOWS];

int event_window(SDL_Event *e, info_window *twindow){
    
    if( e->window.windowID == twindow->idwindow ){
        switch( e->window.event ){
    	    //windowterlihat
            case SDL_WINDOWEVENT_SHOWN:
		        twindow->tutup=salah;
                break;

            //Window menghilang
            case SDL_WINDOWEVENT_HIDDEN:
        	    twindow->tutup=benar;
                break;

            //Get new dimensions and repaint
            case SDL_WINDOWEVENT_SIZE_CHANGED:
                twindow->lebar = e->window.data1;
                twindow->tinggi = e->window.data2;
                SDL_RenderPresent( twindow->mrenderer );
                break;

            case SDL_WINDOWEVENT_EXPOSED:
                SDL_RenderPresent( twindow->mrenderer );
                break;

            //Mouse enter
            case SDL_WINDOWEVENT_ENTER:
        		twindow->
                mMousefocus=benar;
                break;
            
            //Mouse exit
            case SDL_WINDOWEVENT_LEAVE:
		        twindow->
                mMousefocus=salah;
                break;

            //Keyboard focus gained
            case SDL_WINDOWEVENT_FOCUS_GAINED:
		        twindow->
                mKeyboardfocus=benar;
                break;
            
            //Keyboard focus lost
            case SDL_WINDOWEVENT_FOCUS_LOST:
        		twindow->
                mKeyboardfocus=salah;
                break;

            //Window minimized
            case SDL_WINDOWEVENT_MINIMIZED:
        		twindow->
                mMinimized=benar;
                break;

            //Window maxized
            case SDL_WINDOWEVENT_MAXIMIZED:
		        twindow->
                mMinimized=salah;
                break;
            
            //Window restored
            case SDL_WINDOWEVENT_RESTORED:
                twindow->mMinimized=salah;
                break;
            case SDL_WINDOWEVENT_CLOSE:
    	        SDL_HideWindow( twindow->mwindow);
		        break;
	    }
    }
    return 0;
}

int buat_window(const char *nama, info_window *twindow){
    twindow->mwindow=SDL_CreateWindow(nama, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
    if( twindow->mwindow == NULL ){
	printf( "Error : %s", SDL_GetError() );
	return 0;
    }else{
        twindow->mrenderer = SDL_CreateRenderer(twindow->mwindow, -1, SDL_RENDERER_ACCELERATED);
	if(twindow->mrenderer!=NULL){
            SDL_SetRenderDrawColor(twindow->mrenderer, 0xFF, 0xFF, 0xFF, 0xFF);
            twindow->idwindow=SDL_GetWindowID(twindow->mwindow);
	    twindow->tutup=salah;
	}else{
	    SDL_DestroyWindow( twindow->mwindow );
	    twindow->tutup=benar;
	    twindow->mwindow=NULL;
	    return 0;
	}
    }
    return 1; 
}

int main(int argc, char* args[]){
SDL_Event evt;
int hasil;
    if( SDL_Init( SDL_INIT_VIDEO ) < 0){
        printf( "Error: %s\n", SDL_GetError() );
    }else{
	for(int i=0;i<TOTAL_WINDOWS;i++){
	    hasil+=buat_window("SDL_test", &windowku[i]);
	}
	
        if(hasil>=TOTAL_WINDOWS){
	    int berhenti=salah;
            while(berhenti==salah){
		        while(SDL_PollEvent(&evt)){
        		    switch(evt.type){
        			case SDL_WINDOWEVENT :
    	    		    for( int i = 0; i < TOTAL_WINDOWS; i++ ){
			                event_window(&evt, &windowku[i]);
    	    		    }
	            		break;
          			case SDL_KEYDOWN:
			            switch(evt.key.keysym.sym){
			                case SDLK_1:
        				        SDL_ShowWindow(windowku[0].mwindow);
		        		        SDL_RaiseWindow(windowku[0].mwindow);
				                break;
                            case SDLK_2:
            				    SDL_ShowWindow(windowku[1].mwindow);
			            	    SDL_RaiseWindow(windowku[1].mwindow);
            				    break;
			                case SDLK_3:
           			            SDL_ShowWindow(windowku[2].mwindow);
                                SDL_RaiseWindow(windowku[2].mwindow);
 			                    break;
       			        }
		            }
          		}        

		        for(int i=0;i<TOTAL_WINDOWS;i++){
		            SDL_RenderClear(windowku[i].mrenderer);
        		    SDL_RenderPresent(windowku[i].mrenderer);
		        }
		
        		int semua_tutup=benar;
        		for(int i=0;i<TOTAL_WINDOWS;i++){
		            if(windowku[i].tutup==salah){
            			semua_tutup=salah;
		            	break;
	    	        }
        	    }
    	    	if(semua_tutup==benar)berhenti=benar;
	        }
	    }
    }
    for(int i=0;i<3;i++)SDL_DestroyWindow( windowku[i].mwindow );
    SDL_Quit();

    return 0;
}