4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #define GL_GLEXT_PROTOTYPES
23 #include "bcresources.h"
24 #include "bcsignals.h"
25 #include "bcsynchronous.h"
26 #include "bcwindowbase.h"
27 #include "condition.h"
39 TextureID::TextureID(int window_id, int id, int w, int h, int components)
41 this->window_id = window_id;
45 this->components = components;
49 ShaderID::ShaderID(int window_id, unsigned int handle, char *source)
51 this->window_id = window_id;
52 this->handle = handle;
53 this->source = strdup(source);
62 PBufferID::PBufferID(int window_id, GLXPbuffer glx_pbuffer, GLXContext glx_context, int w, int h)
64 this->glx_pbuffer = glx_pbuffer;
65 this->glx_context = glx_context;
66 this->window_id = window_id;
75 BC_SynchronousCommand::BC_SynchronousCommand()
77 command = BC_SynchronousCommand::NONE;
81 command_done = new Condition(0, "BC_SynchronousCommand::command_done", 0);
84 BC_SynchronousCommand::~BC_SynchronousCommand()
89 void BC_SynchronousCommand::copy_from(BC_SynchronousCommand *command)
91 this->command = command->command;
92 this->colormodel = command->colormodel;
93 this->window = command->window;
94 this->frame = command->frame;
95 this->window_id = command->window_id;
96 this->frame_return = command->frame_return;
97 this->id = command->id;
105 BC_Synchronous::BC_Synchronous()
108 lock_sync = new Mutex("BC_Synchronous::lock_sync");
109 next_command = new Condition(0, "BC_Synchronous::next_command", 1);
110 command_lock = new Mutex("BC_Synchronous::command_lock");
111 table_lock = new Mutex("BC_Synchronous::table_lock");
115 BC_WindowBase::get_resources()->set_synchronous(this);
118 BC_Synchronous::~BC_Synchronous()
124 commands.remove_all_objects();
131 void BC_Synchronous::sync_lock(const char *cp)
136 void BC_Synchronous::sync_unlock()
141 BC_SynchronousCommand* BC_Synchronous::new_command()
143 return new BC_SynchronousCommand();
146 void BC_Synchronous::create_objects()
150 void BC_Synchronous::start()
157 void BC_Synchronous::quit()
159 if( !is_started ) return;
161 BC_SynchronousCommand *command = BC_Synchronous::new_command();
162 command->command = BC_SynchronousCommand::QUIT;
163 send_garbage(command);
164 command->command_done->lock("BC_Synchronous::quit");
168 long BC_Synchronous::send_command(BC_SynchronousCommand *command)
170 BC_SynchronousCommand *command2 = new_command();
171 command2->copy_from(command);
172 command_lock->lock("BC_Synchronous::send_command");
173 commands.append(command2);
174 command_lock->unlock();
175 next_command->unlock();
176 //printf("BC_Synchronous::send_command 1 %d\n", next_command->get_value());
178 // Wait for completion
179 command2->command_done->lock("BC_Synchronous::send_command");
180 long result = command2->result;
185 void BC_Synchronous::run()
187 sync_lock("BC_Synchronous::run 0");
189 command_lock->lock("BC_Synchronous::run");
190 BC_SynchronousCommand *command = 0;
192 command = commands.values[0];
193 commands.remove_number(0);
195 command_lock->unlock();
198 next_command->lock("BC_Synchronous::run");
199 sync_lock("BC_Synchronous::run 1");
202 //printf("BC_Synchronous::run %d\n", command->command);
203 handle_command_base(command);
208 void BC_Synchronous::handle_command_base(BC_SynchronousCommand *command)
210 switch(command->command) {
211 case BC_SynchronousCommand::QUIT:
213 command->command_done->unlock();
216 case BC_SynchronousCommand::DELETE_WINDOW:
217 delete_window_sync(command);
218 command->command_done->unlock();
221 case BC_SynchronousCommand::DELETE_PIXMAP:
222 delete_pixmap_sync(command);
225 case BC_SynchronousCommand::DELETE_DISPLAY:
226 delete_display_sync(command);
230 handle_command(command);
231 command->command_done->unlock();
237 void BC_Synchronous::handle_command(BC_SynchronousCommand *command)
241 void BC_Synchronous::put_texture(int id, int w, int h, int components)
245 table_lock->lock("BC_Resources::put_texture");
246 // Search for duplicate
247 for(int i = 0; i < texture_ids.total; i++)
249 TextureID *ptr = texture_ids.values[i];
250 if(ptr->window_id == current_window->get_id() &&
253 printf("BC_Synchronous::push_texture: texture exists\n"
254 "exists: window=%d id=%d w=%d h=%d\n"
255 "new: window=%d id=%d w=%d h=%d\n",
260 current_window->get_id(),
264 table_lock->unlock();
269 TextureID *new_id = new TextureID(current_window->get_id(),
274 texture_ids.append(new_id);
275 table_lock->unlock();
279 int BC_Synchronous::get_texture(int w, int h, int components)
281 table_lock->lock("BC_Resources::get_texture");
282 for(int i = 0; i < texture_ids.total; i++)
284 if(texture_ids.values[i]->w == w &&
285 texture_ids.values[i]->h == h &&
286 texture_ids.values[i]->components == components &&
287 !texture_ids.values[i]->in_use &&
288 texture_ids.values[i]->window_id == current_window->get_id())
290 int result = texture_ids.values[i]->id;
291 texture_ids.values[i]->in_use = 1;
292 table_lock->unlock();
296 table_lock->unlock();
300 void BC_Synchronous::release_texture(int window_id, int id)
302 table_lock->lock("BC_Resources::release_texture");
303 for(int i = 0; i < texture_ids.total; i++)
305 if(texture_ids.values[i]->id == id &&
306 texture_ids.values[i]->window_id == window_id)
308 texture_ids.values[i]->in_use = 0;
309 table_lock->unlock();
313 table_lock->unlock();
320 unsigned int BC_Synchronous::get_shader(char *source, int *got_it)
322 table_lock->lock("BC_Resources::get_shader");
323 for(int i = 0; i < shader_ids.total; i++)
325 if(shader_ids.values[i]->window_id == current_window->get_id() &&
326 !strcmp(shader_ids.values[i]->source, source))
328 unsigned int result = shader_ids.values[i]->handle;
329 table_lock->unlock();
334 table_lock->unlock();
339 void BC_Synchronous::put_shader(unsigned int handle,
342 table_lock->lock("BC_Resources::put_shader");
343 shader_ids.append(new ShaderID(current_window->get_id(), handle, source));
344 table_lock->unlock();
347 void BC_Synchronous::dump_shader(unsigned int handle)
350 table_lock->lock("BC_Resources::dump_shader");
351 for(int i = 0; i < shader_ids.total; i++)
353 if(shader_ids.values[i]->handle == handle)
355 printf("BC_Synchronous::dump_shader\n"
356 "%s", shader_ids.values[i]->source);
361 table_lock->unlock();
362 if(!got_it) printf("BC_Synchronous::dump_shader couldn't find %d\n", handle);
365 void BC_Synchronous::delete_window(BC_WindowBase *window)
368 BC_SynchronousCommand *command = BC_Synchronous::new_command();
369 command->command = BC_SynchronousCommand::DELETE_WINDOW;
370 command->window_id = window->get_id();
371 command->display = window->get_display();
372 command->win = window->win;
373 command->glx_win = window->glx_win;
374 command->glx_context = window->glx_win_context;
376 send_garbage(command);
377 command->command_done->lock("BC_Synchronous::quit");
382 void BC_Synchronous::delete_window_sync(BC_SynchronousCommand *command)
385 int window_id = command->window_id;
386 Display *display = command->display;
387 // Window win = command->win;
388 GLXWindow glx_win = command->glx_win;
389 GLXContext glx_context = command->glx_context;
390 XLockDisplay(display);
393 // texture ID's are unique to different contexts
394 glXMakeContextCurrent(display, glx_win, glx_win, glx_context);
396 table_lock->lock("BC_Resources::release_textures");
397 for(int i = 0; i < texture_ids.total; i++) {
398 if(texture_ids.values[i]->window_id == window_id) {
399 GLuint id = texture_ids.values[i]->id;
400 glDeleteTextures(1, &id);
401 //if(debug) printf("BC_Synchronous::delete_window_sync texture_id=%d window_id=%d\n",
403 texture_ids.remove_object_number(i);
408 for(int i = 0; i < shader_ids.total; i++)
410 if(shader_ids.values[i]->window_id == window_id)
412 glDeleteShader(shader_ids.values[i]->handle);
414 //printf("BC_Synchronous::delete_window_sync shader_id=%d window_id=%d\n",
415 //shader_ids.values[i]->handle, window_id);
416 shader_ids.remove_object_number(i);
421 for(int i = 0; i < pbuffer_ids.total; i++)
423 if(pbuffer_ids.values[i]->window_id == window_id)
425 glXDestroyPbuffer(display, pbuffer_ids.values[i]->glx_pbuffer);
426 glXDestroyContext(display, pbuffer_ids.values[i]->glx_context);
428 //printf("BC_Synchronous::delete_window_sync pbuffer_id=%p window_id=%d\n",
429 // (void*)pbuffer_ids.values[i]->pbuffer, window_id);
430 pbuffer_ids.remove_object_number(i);
436 table_lock->unlock();
438 glXMakeContextCurrent(display, None, None, 0);
440 glXDestroyContext(display, glx_context);
441 // causes xerror BadWindow (invalid Window parameter)
442 // XDestroyWindow(display, glx_win);
443 // win destroyed in bcwindowbase
444 XUnlockDisplay(display);
448 void BC_Synchronous::delete_display(BC_WindowBase *window)
451 BC_SynchronousCommand *command = BC_Synchronous::new_command();
452 command->command = BC_SynchronousCommand::DELETE_DISPLAY;
453 command->display = window->get_display();
455 send_garbage(command);
459 void BC_Synchronous::delete_display_sync(BC_SynchronousCommand *command)
462 Display *display = command->display;
463 XLockDisplay(display);
464 XUnlockDisplay(display);
465 glXMakeContextCurrent(display, None, None, 0);
466 XCloseDisplay(display);
471 void BC_Synchronous::put_pbuffer(int w, int h,
472 GLXPbuffer glx_pbuffer, GLXContext glx_context)
475 table_lock->lock("BC_Resources::release_textures");
476 for(int i = 0; i < pbuffer_ids.total; i++) {
477 PBufferID *ptr = pbuffer_ids.values[i];
478 if( ptr->w == w && ptr->h == h && ptr->glx_pbuffer == glx_pbuffer ) {
485 PBufferID *ptr = new PBufferID(current_window->get_id(),
486 glx_pbuffer, glx_context, w, h);
487 pbuffer_ids.append(ptr);
489 table_lock->unlock();
492 GLXPbuffer BC_Synchronous::get_pbuffer(int w, int h, GLXContext *glx_context)
494 table_lock->lock("BC_Resources::release_textures");
495 for(int i = 0; i < pbuffer_ids.total; i++) {
496 PBufferID *ptr = pbuffer_ids.values[i];
497 if(ptr->w == w && ptr->h == h && !ptr->in_use &&
498 ptr->window_id == current_window->get_id() ) {
499 GLXPbuffer result = ptr->glx_pbuffer;
500 *glx_context = ptr->glx_context;
502 table_lock->unlock();
506 table_lock->unlock();
510 void BC_Synchronous::release_pbuffer(int window_id, GLXPbuffer pbuffer)
512 table_lock->lock("BC_Resources::release_textures");
513 for(int i = 0; i < pbuffer_ids.total; i++) {
514 PBufferID *ptr = pbuffer_ids.values[i];
515 if( ptr->window_id == window_id &&
516 ptr->glx_pbuffer == pbuffer ) {
520 table_lock->unlock();
523 void BC_Synchronous::delete_pixmap(BC_WindowBase *window,
524 GLXPixmap glx_pixmap, GLXContext glx_context)
526 BC_SynchronousCommand *command = BC_Synchronous::new_command();
527 command->command = BC_SynchronousCommand::DELETE_PIXMAP;
528 command->window_id = window->get_id();
529 command->display = window->get_display();
530 command->win = window->win;
531 command->glx_win = window->glx_win;
532 command->glx_pixmap = glx_pixmap;
533 command->glx_context = glx_context;
535 send_garbage(command);
539 void BC_Synchronous::delete_pixmap_sync(BC_SynchronousCommand *command)
542 Display *display = command->display;
543 GLXWindow glx_win = command->glx_win;
544 XLockDisplay(display);
545 glXMakeContextCurrent(display, glx_win, glx_win, command->glx_context);
546 glXDestroyContext(display, command->glx_context);
547 glXDestroyGLXPixmap(display, command->glx_pixmap);
548 XUnlockDisplay(display);
554 void BC_Synchronous::send_garbage(BC_SynchronousCommand *command)
556 command_lock->lock("BC_Synchronous::send_garbage");
557 commands.append(command);
558 command_lock->unlock();
559 next_command->unlock();
562 BC_WindowBase* BC_Synchronous::get_window()
564 return current_window;