2012-02-12 04:54:34 +04:00
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*
* Window Library
*/
# include <syscall.h>
# include <stdint.h>
# include <stdio.h>
# include <unistd.h>
# include <errno.h>
# include <string.h>
# include <assert.h>
# include <sys/stat.h>
2013-05-06 02:00:24 +04:00
# include <signal.h>
2012-02-12 04:54:34 +04:00
# include "window.h"
2012-04-13 07:42:24 +04:00
# include "pthread.h"
2012-02-12 04:54:34 +04:00
2012-11-22 11:29:57 +04:00
extern FILE * fdopen ( int fildes , const char * mode ) ;
2012-03-21 01:23:24 +04:00
2012-12-10 11:07:04 +04:00
static int event_pipe ;
static int mouse_event_pipe ;
static int gobble_mouse_events = 1 ;
2012-03-02 07:13:52 +04:00
# define LOCK(lock) while (__sync_lock_test_and_set(&lock, 0x01)) { syscall_yield(); };
2012-02-12 04:54:34 +04:00
# define UNLOCK(lock) __sync_lock_release(&lock);
2012-02-13 02:45:23 +04:00
# define WIN_B 4
2012-02-12 04:54:34 +04:00
volatile wins_server_global_t * wins_globals = NULL ;
process_windows_t * process_windows = NULL ;
2013-04-08 03:21:00 +04:00
# define get_window wins_get_window
window_t * wins_get_window ( wid_t wid ) {
2012-02-12 04:54:34 +04:00
foreach ( n , process_windows - > windows ) {
window_t * w = ( window_t * ) n - > value ;
if ( w - > wid = = wid ) {
return w ;
}
}
return NULL ;
}
2012-10-14 00:02:58 +04:00
void ( * mouse_action_callback ) ( w_mouse_t * ) = NULL ;
void ( * resize_window_callback ) ( window_t * ) = NULL ;
2012-11-20 09:24:21 +04:00
void ( * focus_changed_callback ) ( window_t * ) = NULL ;
2012-04-13 10:21:01 +04:00
2012-02-12 04:54:34 +04:00
/* Window Object Management */
2012-02-13 05:30:36 +04:00
/*XXX ... */
window_t * init_window_client ( process_windows_t * pw , wid_t wid , int32_t x , int32_t y , uint16_t width , uint16_t height , uint16_t index ) {
window_t * window = malloc ( sizeof ( window_t ) ) ;
if ( ! window ) {
fprintf ( stderr , " [%d] [window] Could not malloc a window_t! " , getpid ( ) ) ;
return NULL ;
}
window - > owner = pw ;
window - > wid = wid ;
window - > bufid = 0 ;
window - > width = width ;
window - > height = height ;
2012-11-22 11:29:57 +04:00
window - > focused = 0 ;
2012-02-13 05:30:36 +04:00
char key [ 1024 ] ;
SHMKEY_ ( key , 1024 , window ) ;
2012-02-13 02:45:23 +04:00
2012-02-16 13:34:42 +04:00
size_t size = ( width * height * WIN_B ) ;
window - > buffer = ( uint8_t * ) syscall_shm_obtain ( key , & size ) ;
2012-02-13 02:45:23 +04:00
2012-02-12 04:54:34 +04:00
if ( ! window - > buffer ) {
fprintf ( stderr , " [%d] [window] Could not create a buffer for a new window for pid %d! " , getpid ( ) , pw - > pid ) ;
free ( window ) ;
return NULL ;
}
list_insert ( pw - > windows , window ) ;
return window ;
}
2012-03-21 01:23:24 +04:00
void free_window_client ( window_t * window ) {
2012-02-12 04:54:34 +04:00
/* Free the window buffer */
2012-02-26 08:47:20 +04:00
if ( ! window ) return ;
2012-02-12 04:54:34 +04:00
char key [ 256 ] ;
SHMKEY ( key , 256 , window ) ;
syscall_shm_release ( key ) ;
/* Now, kill the object itself */
process_windows_t * pw = window - > owner ;
2012-03-08 09:44:02 +04:00
node_t * n = list_find ( pw - > windows , window ) ;
if ( n ) {
2012-03-21 01:23:24 +04:00
list_delete ( pw - > windows , n ) ;
free ( n ) ;
2012-03-08 09:44:02 +04:00
}
2012-03-21 01:23:24 +04:00
}
2012-10-14 00:02:58 +04:00
void resize_window_buffer_client ( window_t * window , int16_t left , int16_t top , uint16_t width , uint16_t height ) {
2012-03-02 07:13:52 +04:00
2012-10-14 00:02:58 +04:00
if ( ! window ) {
return ;
}
/* If the window has enlarged, we need to create a new buffer */
if ( ( width * height ) > ( window - > width * window - > height ) ) {
/* Release the old buffer */
char key [ 256 ] , keyn [ 256 ] ;
SHMKEY_ ( key , 256 , window ) ;
/* Create the new one */
window - > bufid + + ;
SHMKEY_ ( keyn , 256 , window ) ;
2012-03-02 07:13:52 +04:00
2012-02-16 13:34:42 +04:00
size_t size = ( width * height * WIN_B ) ;
2012-10-14 00:02:58 +04:00
char * new_buffer = ( uint8_t * ) syscall_shm_obtain ( keyn , & size ) ;
window - > buffer = new_buffer ;
2012-10-14 07:19:43 +04:00
syscall_shm_release ( key ) ;
2012-02-12 04:54:34 +04:00
}
window - > width = width ;
window - > height = height ;
}
/* Command Dispatch */
uint8_t volatile wins_command_lock ;
uint8_t volatile wins_command_recvd ;
window_t volatile * wins_last_new ;
2012-02-14 05:46:00 +04:00
void wins_send_command ( wid_t wid , int16_t left , int16_t top , uint16_t width , uint16_t height , int command , int wait_for_reply ) {
2012-02-12 04:54:34 +04:00
/* Construct the header and packet */
wins_packet_t header ;
2012-02-21 09:31:00 +04:00
header . magic = WINS_MAGIC ;
2012-02-12 04:54:34 +04:00
header . command_type = command ;
header . packet_size = sizeof ( w_window_t ) ;
w_window_t packet ;
packet . wid = wid ;
packet . left = left ;
packet . top = top ;
packet . width = width ;
packet . height = height ;
/* Send them */
LOCK ( wins_command_lock ) ;
wins_command_recvd = 0xFF ; // XXX: Will this work?
2012-03-21 01:23:24 +04:00
fwrite ( & header , sizeof ( wins_packet_t ) , 1 , process_windows - > command_pipe_file ) ;
fwrite ( & packet , sizeof ( w_window_t ) , 1 , process_windows - > command_pipe_file ) ;
fflush ( process_windows - > command_pipe_file ) ;
2012-02-12 04:54:34 +04:00
/* Now wait for the command to be processed before returning */
if ( wait_for_reply ) {
2012-02-13 05:30:36 +04:00
syscall_send_signal ( process_windows - > pid , SIGWINEVENT ) ;
2012-03-02 07:13:52 +04:00
while ( ( wins_command_recvd & 0xF ) ! = ( command & 0xF ) ) {
syscall_yield ( ) ;
}
2012-02-12 04:54:34 +04:00
}
UNLOCK ( wins_command_lock ) ;
}
2012-02-14 05:46:00 +04:00
window_t * window_create ( int16_t left , int16_t top , uint16_t width , uint16_t height ) {
2012-02-12 04:54:34 +04:00
wins_send_command ( 0 , left , top , width , height , WC_NEWWINDOW , 1 ) ;
2012-03-02 07:13:52 +04:00
while ( ! wins_last_new ) {
syscall_yield ( ) ;
}
2012-02-14 02:21:52 +04:00
2012-02-12 04:54:34 +04:00
return ( window_t * ) wins_last_new ;
}
2012-02-14 05:46:00 +04:00
void window_resize ( window_t * window , int16_t left , int16_t top , uint16_t width , uint16_t height ) {
2012-02-12 04:54:34 +04:00
wins_send_command ( window - > wid , left , top , width , height , WC_RESIZE , 1 ) ;
}
2012-02-14 05:46:00 +04:00
void window_redraw ( window_t * window , int16_t left , int16_t top , uint16_t width , uint16_t height ) {
2012-02-12 04:54:34 +04:00
wins_send_command ( window - > wid , left , top , width , height , WC_DAMAGE , 0 ) ;
}
void window_redraw_full ( window_t * window ) {
wins_send_command ( window - > wid , 0 , 0 , window - > width , window - > height , WC_DAMAGE , 0 ) ;
}
2012-03-02 07:13:52 +04:00
void window_redraw_wait ( window_t * window ) {
wins_send_command ( window - > wid , 0 , 0 , window - > width , window - > height , WC_REDRAW , 1 ) ;
}
2012-02-12 04:54:34 +04:00
void window_destroy ( window_t * window ) {
2012-03-24 02:44:37 +04:00
wins_send_command ( window - > wid , 0 , 0 , 0 , 0 , WC_DESTROY , 1 ) ;
2012-03-21 01:23:24 +04:00
free_window_client ( window ) ;
2012-02-12 04:54:34 +04:00
}
2012-03-08 08:31:24 +04:00
void window_reorder ( window_t * window , uint16_t new_zed ) {
wins_send_command ( window - > wid , new_zed , 0 , 0 , 0 , WC_REORDER , 0 ) ;
}
2012-09-14 09:12:47 +04:00
void window_enable_alpha ( window_t * window ) {
2012-10-12 10:55:53 +04:00
wins_send_command ( window - > wid , 1 , 0 , 0 , 0 , WC_SET_ALPHA , 0 ) ;
}
void window_disable_alpha ( window_t * window ) {
2012-09-14 09:12:47 +04:00
wins_send_command ( window - > wid , 0 , 0 , 0 , 0 , WC_SET_ALPHA , 0 ) ;
}
2012-02-12 04:54:34 +04:00
/* Event Processing (invoked by signal only) */
2012-12-10 11:07:04 +04:00
# define MAX_UNREAD_KEY_EVENTS 200
# define MAX_UNREAD_MOUSE_EVENTS 200
static int kbd_read = 0 ;
static int mouse_read = 0 ;
2012-02-12 04:54:34 +04:00
w_keyboard_t * poll_keyboard ( ) {
2012-02-17 11:28:12 +04:00
w_keyboard_t * evt = NULL ;
2012-02-12 04:54:34 +04:00
2012-12-10 11:07:04 +04:00
read ( event_pipe , & evt , sizeof ( evt ) ) ;
kbd_read - - ;
return evt ;
}
w_keyboard_t * poll_keyboard_async ( ) {
w_keyboard_t * evt = NULL ;
struct stat _stat ;
fstat ( event_pipe , & _stat ) ;
if ( _stat . st_size > = sizeof ( evt ) ) {
read ( event_pipe , & evt , sizeof ( evt ) ) ;
kbd_read - - ;
2012-02-12 04:54:34 +04:00
}
return evt ;
}
static void process_key_evt ( uint8_t command , w_keyboard_t * evt ) {
/* Push the event onto a buffer for the process to poll */
2012-12-10 11:07:04 +04:00
kbd_read + + ;
if ( kbd_read > MAX_UNREAD_KEY_EVENTS ) {
char garbage [ sizeof ( evt ) ] ;
read ( event_pipe , & garbage , sizeof ( evt ) ) ;
kbd_read - - ;
}
2012-02-12 04:54:34 +04:00
2012-12-10 11:07:04 +04:00
write ( event_pipe , & evt , sizeof ( evt ) ) ;
}
2012-02-12 04:54:34 +04:00
w_mouse_t * poll_mouse ( ) {
2012-04-13 07:42:24 +04:00
w_mouse_t * evt = NULL ;
2012-02-12 04:54:34 +04:00
2012-12-10 11:07:04 +04:00
read ( mouse_event_pipe , & evt , sizeof ( evt ) ) ;
mouse_read - - ;
2012-02-12 04:54:34 +04:00
return evt ;
}
static void process_mouse_evt ( uint8_t command , w_mouse_t * evt ) {
/* Push the event onto a buffer for the process to poll */
2012-12-10 11:07:04 +04:00
if ( gobble_mouse_events ) return ; /* om nom nom */
mouse_read + + ;
if ( mouse_read > MAX_UNREAD_MOUSE_EVENTS ) {
char garbage [ sizeof ( evt ) ] ;
read ( mouse_event_pipe , & garbage , sizeof ( evt ) ) ;
mouse_read - - ;
2012-04-13 07:42:24 +04:00
}
2012-04-13 10:21:01 +04:00
if ( mouse_action_callback ) {
mouse_action_callback ( evt ) ;
}
2012-12-10 11:07:04 +04:00
write ( mouse_event_pipe , & evt , sizeof ( evt ) ) ;
2012-02-12 04:54:34 +04:00
}
static void process_window_evt ( uint8_t command , w_window_t evt ) {
switch ( command ) {
window_t * window = NULL ;
case WE_NEWWINDOW :
2012-02-13 05:30:36 +04:00
window = init_window_client ( process_windows , evt . wid , evt . left , evt . top , evt . width , evt . height , 0 ) ;
2012-02-12 04:54:34 +04:00
wins_last_new = window ;
break ;
2012-11-20 09:24:21 +04:00
case WE_FOCUSCHG :
window = get_window ( evt . wid ) ;
2012-12-08 12:24:43 +04:00
if ( window ) {
window - > focused = evt . left ;
if ( focus_changed_callback ) {
focus_changed_callback ( window ) ;
}
2012-11-20 09:24:21 +04:00
}
break ;
2012-02-12 04:54:34 +04:00
case WE_RESIZED :
/* XXX: We need a lock or something to contend the window buffer */
window = get_window ( evt . wid ) ;
if ( ! window ) {
fprintf ( stderr , " [%d] [window] SEVERE: wins sent WE_RESIZED for window we don't have! \n " , getpid ( ) ) ;
2012-11-22 11:29:57 +04:00
return ;
2012-02-12 04:54:34 +04:00
}
2012-10-14 00:02:58 +04:00
resize_window_buffer_client ( window , evt . left , evt . top , evt . width , evt . height ) ;
if ( resize_window_callback ) {
resize_window_callback ( window ) ;
}
2012-02-12 04:54:34 +04:00
break ;
}
wins_command_recvd = command ;
}
2012-12-10 11:07:04 +04:00
static void process_evt ( ) {
/* Wait for and process one event */
2012-02-12 04:54:34 +04:00
2012-12-10 11:07:04 +04:00
wins_packet_t header ;
read ( process_windows - > event_pipe , & header , sizeof ( wins_packet_t ) ) ;
while ( header . magic ! = WINS_MAGIC ) {
/* REALIGN!! */
memcpy ( & header , ( void * ) ( ( uintptr_t ) & header + 1 ) , ( sizeof ( header ) - 1 ) ) ;
read ( process_windows - > event_pipe , ( char * ) ( ( uintptr_t ) & header + sizeof ( header ) - 1 ) , 1 ) ;
}
2012-02-12 04:54:34 +04:00
2012-12-10 11:07:04 +04:00
/* Determine type, read, and dispatch */
switch ( header . command_type & WE_GROUP_MASK ) {
case WE_MOUSE_EVT : {
w_mouse_t * mevt = malloc ( sizeof ( w_mouse_t ) ) ;
read ( process_windows - > event_pipe , mevt , sizeof ( w_mouse_t ) ) ;
process_mouse_evt ( header . command_type , mevt ) ;
break ;
2012-02-21 09:31:00 +04:00
}
2012-02-17 11:28:12 +04:00
2012-12-10 11:07:04 +04:00
case WE_KEY_EVT : {
w_keyboard_t * kevt = malloc ( sizeof ( w_keyboard_t ) ) ;
read ( process_windows - > event_pipe , kevt , sizeof ( w_keyboard_t ) ) ;
process_key_evt ( header . command_type , kevt ) ;
break ;
}
2012-02-12 04:54:34 +04:00
2012-12-10 11:07:04 +04:00
case WE_WINDOW_EVT : {
w_window_t wevt ;
read ( process_windows - > event_pipe , & wevt , sizeof ( w_window_t ) ) ;
process_window_evt ( header . command_type , wevt ) ;
break ;
}
2012-02-12 04:54:34 +04:00
2012-12-10 11:07:04 +04:00
default :
fprintf ( stderr , " [%d] [window] WARN: Received unknown event type %d, 0x%x \n " , getpid ( ) , header . command_type , header . packet_size ) ;
struct stat buf ;
fstat ( process_windows - > event_pipe , & buf ) ;
char devnull [ 1 ] ;
for ( uint32_t i = 0 ; i < buf . st_size ; + + i ) {
read ( process_windows - > event_pipe , devnull , 1 ) ;
2012-02-12 04:54:34 +04:00
}
2012-12-10 11:07:04 +04:00
break ;
}
}
2012-02-12 04:54:34 +04:00
2012-12-10 11:07:04 +04:00
static void sig_process_evt ( int sig ) {
if ( ! process_windows ) return ;
struct stat buf ;
fstat ( process_windows - > event_pipe , & buf ) ;
do {
process_evt ( ) ;
2012-02-12 04:54:34 +04:00
fstat ( process_windows - > event_pipe , & buf ) ;
2012-12-10 11:07:04 +04:00
} while ( buf . st_size ) ;
wins_packet_t header ;
2012-02-12 04:54:34 +04:00
}
void install_signal_handlers ( ) {
2012-12-10 11:07:04 +04:00
syscall_signal ( SIGWINEVENT , sig_process_evt ) ; // SIGWINEVENT
2012-02-12 04:54:34 +04:00
}
2012-04-13 07:42:24 +04:00
static void ignore ( int sig ) {
return ;
}
void * win_threaded_event_processor ( void * garbage ) {
while ( 1 ) {
2012-12-11 09:19:13 +04:00
process_evt ( ) ;
2012-04-13 07:42:24 +04:00
}
}
2013-04-10 11:18:40 +04:00
static int _disabled_sigwinevent = 0 ;
2012-04-13 07:42:24 +04:00
void win_use_threaded_handler ( ) {
2013-04-10 11:18:40 +04:00
_disabled_sigwinevent = 1 ;
2012-09-05 07:27:49 +04:00
syscall_signal ( SIGWINEVENT , ignore ) ; // SIGWINEVENT
2012-04-13 07:42:24 +04:00
pthread_t event_thread ;
pthread_create ( & event_thread , NULL , win_threaded_event_processor , NULL ) ;
2012-12-10 11:07:04 +04:00
gobble_mouse_events = 0 ;
2012-04-13 07:42:24 +04:00
}
2012-02-12 04:54:34 +04:00
2013-04-08 03:21:00 +04:00
void win_sane_events ( ) {
2013-04-10 11:18:40 +04:00
_disabled_sigwinevent = 1 ;
2013-04-08 03:21:00 +04:00
syscall_signal ( SIGWINEVENT , ignore ) ; // SIGWINEVENT
gobble_mouse_events = 0 ;
}
wins_packet_t * get_window_events ( ) {
/* Wait for and return one window event; process the core window events, but also return them for reference */
wins_packet_t header ;
read ( process_windows - > event_pipe , & header , sizeof ( wins_packet_t ) ) ;
while ( header . magic ! = WINS_MAGIC ) {
/* REALIGN!! */
memcpy ( & header , ( void * ) ( ( uintptr_t ) & header + 1 ) , ( sizeof ( header ) - 1 ) ) ;
read ( process_windows - > event_pipe , ( char * ) ( ( uintptr_t ) & header + sizeof ( header ) - 1 ) , 1 ) ;
}
wins_packet_t * out = malloc ( sizeof ( wins_packet_t ) + header . packet_size ) ;
memcpy ( out , & header , sizeof ( wins_packet_t ) ) ;
read ( process_windows - > event_pipe , ( char * ) ( ( uintptr_t ) out + sizeof ( wins_packet_t ) ) , header . packet_size ) ;
return out ;
}
2013-04-10 11:10:00 +04:00
wins_packet_t * get_window_events_async ( ) {
struct stat _stat ;
fstat ( process_windows - > event_pipe , & _stat ) ;
if ( _stat . st_size > = sizeof ( wins_packet_t ) ) {
return get_window_events ( ) ;
} else {
return NULL ;
}
}
2013-04-08 03:21:00 +04:00
2012-02-12 04:54:34 +04:00
/* Initial Connection */
int wins_connect ( ) {
if ( wins_globals ) {
/* Already connected. Bailing. */
return 0 ;
}
2012-02-16 13:34:42 +04:00
size_t size = sizeof ( wins_server_global_t ) ;
wins_globals = ( volatile wins_server_global_t * ) syscall_shm_obtain ( WINS_SERVER_IDENTIFIER , & size ) ;
2012-02-12 04:54:34 +04:00
if ( ! wins_globals ) {
fprintf ( stderr , " [%d] [window] Unable to connect with wins through shared memory. \n " , getpid ( ) ) ;
return EACCES ;
}
/* Verify magic */
if ( wins_globals - > magic ! = WINS_MAGIC ) {
/* If the magic is incorrent, this probably means the server isn't available. */
2012-03-15 00:04:12 +04:00
fprintf ( stderr , " [%d] [window] Window server [%p] size claims to be %dx%d \n " , getpid ( ) , wins_globals , wins_globals - > server_width , wins_globals - > server_height ) ;
2012-02-12 04:54:34 +04:00
fprintf ( stderr , " [%d] [window] Window server not available (expected magic %x, got %x) \n " , getpid ( ) , WINS_MAGIC , wins_globals - > magic ) ;
syscall_shm_release ( WINS_SERVER_IDENTIFIER ) ;
return EAGAIN ;
}
/* Enter handshake lock */
LOCK ( wins_globals - > lock ) ;
/* Lock Obtained */
/* Share client PID */
wins_globals - > client_pid = getpid ( ) ;
wins_globals - > server_done = 0 ;
/* Mark us as done and wait for the server */
wins_globals - > client_done = 1 ;
2012-02-14 02:21:52 +04:00
2012-03-02 07:13:52 +04:00
while ( ! wins_globals - > server_done ) {
syscall_yield ( ) ;
}
2012-02-12 04:54:34 +04:00
2012-02-14 02:21:52 +04:00
2012-02-12 04:54:34 +04:00
assert ( process_windows & & " process_windows was not initialized! " ) ;
process_windows - > pid = wins_globals - > server_pid ;
process_windows - > event_pipe = syscall_get_fd ( wins_globals - > event_pipe ) ;
process_windows - > command_pipe = syscall_get_fd ( wins_globals - > command_pipe ) ;
2012-03-21 01:23:24 +04:00
process_windows - > command_pipe_file = fdopen ( process_windows - > command_pipe , " w " ) ;
2012-02-12 04:54:34 +04:00
2012-02-13 04:47:01 +04:00
if ( process_windows - > event_pipe < 0 ) {
2012-02-14 02:21:52 +04:00
fprintf ( stderr , " ERROR: Failed to initialize an event pipe! \n " ) ;
return 1 ;
2012-02-13 04:47:01 +04:00
}
2012-02-12 04:54:34 +04:00
/* Reset client status for next client */
wins_globals - > client_done = 0 ;
wins_globals - > event_pipe = 0 ;
wins_globals - > command_pipe = 0 ;
wins_globals - > client_pid = 0 ;
wins_globals - > server_done = 0 ;
/* Done with lock */
UNLOCK ( wins_globals - > lock ) ;
return 0 ;
}
int wins_disconnect ( ) {
2012-03-24 02:44:37 +04:00
#if 0
2012-02-12 04:54:34 +04:00
syscall_shm_release ( WINS_SERVER_IDENTIFIER ) ;
2012-03-24 02:44:37 +04:00
# endif
2012-02-12 04:54:34 +04:00
if ( wins_globals ) {
2012-03-21 01:23:24 +04:00
//free((wins_server_global_t *)wins_globals);
2012-02-12 04:54:34 +04:00
wins_globals = NULL ;
}
}
/* Client Setup/Teardown */
int setup_windowing ( ) {
if ( ! process_windows ) {
process_windows = malloc ( sizeof ( process_windows_t ) ) ;
process_windows - > windows = list_create ( ) ;
}
2012-12-10 11:07:04 +04:00
event_pipe = syscall_mkpipe ( ) ;
mouse_event_pipe = syscall_mkpipe ( ) ;
2012-12-10 12:29:04 +04:00
install_signal_handlers ( ) ;
2012-02-12 04:54:34 +04:00
return wins_connect ( ) ;
}
void teardown_windowing ( ) {
if ( process_windows ) {
2013-04-10 11:18:40 +04:00
if ( _disabled_sigwinevent ) {
syscall_signal ( SIGWINEVENT , sig_process_evt ) ; // SIGWINEVENT
}
2012-02-12 04:54:34 +04:00
window_t * window ;
2012-03-24 02:44:37 +04:00
node_t * node ;
while ( ( node = list_pop ( process_windows - > windows ) ) ! = NULL ) {
window = node - > value ;
2012-02-26 08:47:20 +04:00
if ( ! window ) break ;
2012-02-12 04:54:34 +04:00
window_destroy ( window ) ;
}
free ( process_windows - > windows ) ;
free ( process_windows ) ;
process_windows = NULL ;
}
wins_disconnect ( ) ;
}