00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "global.hpp"
00022
00023 #include "font.hpp"
00024 #include "foreach.hpp"
00025 #include "image.hpp"
00026 #include "log.hpp"
00027 #include "preferences.hpp"
00028 #include "preferences_display.hpp"
00029 #include "sdl_utils.hpp"
00030 #include "video.hpp"
00031
00032 #include <vector>
00033 #include <map>
00034 #include <algorithm>
00035
00036 static lg::log_domain log_display("display");
00037 #define LOG_DP LOG_STREAM(info, log_display)
00038 #define ERR_DP LOG_STREAM(err, log_display)
00039
00040 namespace {
00041 bool fullScreen = false;
00042 int disallow_resize = 0;
00043 }
00044 void resize_monitor::process(events::pump_info &info) {
00045 if(info.resize_dimensions.first >= preferences::min_allowed_width()
00046 && info.resize_dimensions.second >= preferences::min_allowed_height()
00047 && disallow_resize == 0) {
00048 preferences::set_resolution(info.resize_dimensions);
00049 }
00050 }
00051
00052 resize_lock::resize_lock()
00053 {
00054 ++disallow_resize;
00055 }
00056
00057 resize_lock::~resize_lock()
00058 {
00059 --disallow_resize;
00060 }
00061
00062 static unsigned int get_flags(unsigned int flags)
00063 {
00064
00065
00066 #if !(defined(_WIN32) || defined(__APPLE__) || defined(__AMIGAOS4__))
00067 flags |= SDL_HWSURFACE;
00068 #endif
00069 if((flags&SDL_FULLSCREEN) == 0)
00070 flags |= SDL_RESIZABLE;
00071
00072 return flags;
00073 }
00074
00075 namespace {
00076 struct event {
00077 int x, y, w, h;
00078 bool in;
00079 event(const SDL_Rect& rect, bool i) : x(i ? rect.x : rect.x + rect.w), y(rect.y), w(rect.w), h(rect.h), in(i) { }
00080 };
00081 bool operator<(const event& a, const event& b) {
00082 if (a.x != b.x) return a.x < b.x;
00083 if (a.in != b.in) return a.in;
00084 if (a.y != b.y) return a.y < b.y;
00085 if (a.h != b.h) return a.h < b.h;
00086 if (a.w != b.w) return a.w < b.w;
00087 return false;
00088 }
00089 bool operator==(const event& a, const event& b) {
00090 return a.x == b.x && a.y == b.y && a.w == b.w && a.h == b.h && a.in == b.in;
00091 }
00092
00093 struct segment {
00094 int x, count;
00095 segment() : x(0), count(0) { }
00096 segment(int x, int count) : x(x), count(count) { }
00097 };
00098
00099
00100 std::vector<SDL_Rect> update_rects;
00101 std::vector<event> events;
00102 std::map<int, segment> segments;
00103
00104 static void calc_rects()
00105 {
00106 events.clear();
00107
00108 foreach (SDL_Rect const &rect, update_rects) {
00109 events.push_back(event(rect, true));
00110 events.push_back(event(rect, false));
00111 }
00112
00113 std::sort(events.begin(), events.end());
00114 std::vector<event>::iterator events_end = std::unique(events.begin(), events.end());
00115
00116 segments.clear();
00117 update_rects.clear();
00118
00119 for (std::vector<event>::iterator iter = events.begin(); iter != events_end; ++iter) {
00120 std::map<int, segment>::iterator lower = segments.find(iter->y);
00121 if (lower == segments.end()) {
00122 lower = segments.insert(std::make_pair(iter->y, segment())).first;
00123 if (lower != segments.begin()) {
00124 std::map<int, segment>::iterator prev = lower;
00125 --prev;
00126 lower->second = prev->second;
00127 }
00128 }
00129
00130 if (lower->second.count == 0) {
00131 lower->second.x = iter->x;
00132 }
00133
00134 std::map<int, segment>::iterator upper = segments.find(iter->y + iter->h);
00135 if (upper == segments.end()) {
00136 upper = segments.insert(std::make_pair(iter->y + iter->h, segment())).first;
00137 std::map<int, segment>::iterator prev = upper;
00138 --prev;
00139 upper->second = prev->second;
00140 }
00141
00142 if (iter->in) {
00143 while (lower != upper) {
00144 ++lower->second.count;
00145 ++lower;
00146 }
00147 } else {
00148 while (lower != upper) {
00149 lower->second.count--;
00150 if (lower->second.count == 0) {
00151 std::map<int, segment>::iterator next = lower;
00152 ++next;
00153
00154 int x = lower->second.x, y = lower->first;
00155 unsigned w = iter->x - x;
00156 unsigned h = next->first - y;
00157 SDL_Rect a = create_rect(x, y, w, h);
00158
00159 if (update_rects.empty()) {
00160 update_rects.push_back(a);
00161 } else {
00162 SDL_Rect& p = update_rects.back(), n;
00163 int pa = p.w * p.h, aa = w * h, s = pa + aa;
00164 int thresh = 51;
00165
00166 n.w = std::max<int>(x + w, p.x + p.w);
00167 n.x = std::min<int>(p.x, x);
00168 n.w -= n.x;
00169 n.h = std::max<int>(y + h, p.y + p.h);
00170 n.y = std::min<int>(p.y, y);
00171 n.h -= n.y;
00172
00173 if (s * 100 < thresh * n.w * n.h) {
00174 update_rects.push_back(a);
00175 } else {
00176 p = n;
00177 }
00178 }
00179
00180 if (lower == segments.begin()) {
00181 segments.erase(lower);
00182 } else {
00183 std::map<int, segment>::iterator prev = lower;
00184 --prev;
00185 if (prev->second.count == 0) segments.erase(lower);
00186 }
00187
00188 lower = next;
00189 } else {
00190 ++lower;
00191 }
00192 }
00193 }
00194 }
00195 }
00196
00197
00198 bool update_all = false;
00199 }
00200
00201 static void clear_updates()
00202 {
00203 update_all = false;
00204 update_rects.clear();
00205 }
00206
00207 namespace {
00208
00209 surface frameBuffer = NULL;
00210 bool fake_interactive = false;
00211 }
00212
00213 bool non_interactive()
00214 {
00215 if (fake_interactive)
00216 return false;
00217 return SDL_GetVideoSurface() == NULL;
00218 }
00219
00220 surface display_format_alpha(surface surf)
00221 {
00222 if(SDL_GetVideoSurface() != NULL)
00223 return SDL_DisplayFormatAlpha(surf);
00224 else if(frameBuffer != NULL)
00225 return SDL_ConvertSurface(surf,frameBuffer->format,0);
00226 else
00227 return NULL;
00228 }
00229
00230 surface get_video_surface()
00231 {
00232 return frameBuffer;
00233 }
00234
00235 SDL_Rect screen_area()
00236 {
00237 return create_rect(0, 0, frameBuffer->w, frameBuffer->h);
00238 }
00239
00240 void update_rect(size_t x, size_t y, size_t w, size_t h)
00241 {
00242 update_rect(create_rect(x, y, w, h));
00243 }
00244
00245 void update_rect(const SDL_Rect& rect_value)
00246 {
00247 if(update_all)
00248 return;
00249
00250 SDL_Rect rect = rect_value;
00251
00252 surface const fb = SDL_GetVideoSurface();
00253 if(fb != NULL) {
00254 if(rect.x < 0) {
00255 if(rect.x*-1 >= int(rect.w))
00256 return;
00257
00258 rect.w += rect.x;
00259 rect.x = 0;
00260 }
00261
00262 if(rect.y < 0) {
00263 if(rect.y*-1 >= int(rect.h))
00264 return;
00265
00266 rect.h += rect.y;
00267 rect.y = 0;
00268 }
00269
00270 if(rect.x + rect.w > fb->w) {
00271 rect.w = fb->w - rect.x;
00272 }
00273
00274 if(rect.y + rect.h > fb->h) {
00275 rect.h = fb->h - rect.y;
00276 }
00277
00278 if(rect.x >= fb->w) {
00279 return;
00280 }
00281
00282 if(rect.y >= fb->h) {
00283 return;
00284 }
00285 }
00286
00287 update_rects.push_back(rect);
00288 }
00289
00290 void update_whole_screen()
00291 {
00292 update_all = true;
00293 }
00294 CVideo::CVideo(FAKE_TYPES type) : mode_changed_(false), bpp_(0), fake_screen_(false), help_string_(0), updatesLocked_(0)
00295 {
00296 initSDL();
00297 switch(type)
00298 {
00299 case NO_FAKE:
00300 break;
00301 case FAKE:
00302 make_fake();
00303 break;
00304 case FAKE_TEST:
00305 make_test_fake();
00306 break;
00307 }
00308 }
00309
00310 void CVideo::initSDL()
00311 {
00312 const int res = SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
00313
00314 if(res < 0) {
00315 ERR_DP << "Could not initialize SDL_video: " << SDL_GetError() << "\n";
00316 throw CVideo::error();
00317 }
00318 }
00319
00320 CVideo::~CVideo()
00321 {
00322 LOG_DP << "calling SDL_Quit()\n";
00323 SDL_Quit();
00324 LOG_DP << "called SDL_Quit()\n";
00325 }
00326
00327 void CVideo::blit_surface(int x, int y, surface surf, SDL_Rect* srcrect, SDL_Rect* clip_rect)
00328 {
00329 surface target(getSurface());
00330 SDL_Rect dst = create_rect(x, y, 0, 0);
00331
00332 const clip_rect_setter clip_setter(target, clip_rect, clip_rect != NULL);
00333 sdl_blit(surf,srcrect,target,&dst);
00334 }
00335
00336 void CVideo::make_fake()
00337 {
00338 fake_screen_ = true;
00339 frameBuffer = SDL_CreateRGBSurface(SDL_SWSURFACE,16,16,24,0xFF0000,0xFF00,0xFF,0);
00340 image::set_pixel_format(frameBuffer->format);
00341 }
00342
00343 void CVideo::make_test_fake(const unsigned width,
00344 const unsigned height, const unsigned bpp)
00345 {
00346 frameBuffer = SDL_CreateRGBSurface(SDL_SWSURFACE,
00347 width, height, bpp, 0xFF0000, 0xFF00, 0xFF, 0);
00348 image::set_pixel_format(frameBuffer->format);
00349
00350 fake_interactive = true;
00351
00352 }
00353
00354 int CVideo::bppForMode( int x, int y, int flags)
00355 {
00356 int test_values[3] = {getBpp(), 32, 16};
00357 foreach(int &bpp, test_values) {
00358 if(modePossible(x, y, bpp, flags) > 0) {
00359 return bpp;
00360 }
00361 }
00362
00363 return 0;
00364 }
00365
00366 int CVideo::modePossible( int x, int y, int bits_per_pixel, int flags, bool current_screen_optimal )
00367 {
00368
00369 int bpp = SDL_VideoModeOK( x, y, bits_per_pixel, get_flags(flags) );
00370 if(current_screen_optimal)
00371 {
00372 const SDL_VideoInfo* const video_info = SDL_GetVideoInfo();
00373
00374
00375
00376
00377 if(bpp==0 && video_info->current_h<y && video_info->current_w<x){
00378 return bits_per_pixel;
00379 }
00380 }
00381 return bpp;
00382 }
00383
00384 int CVideo::setMode( int x, int y, int bits_per_pixel, int flags )
00385 {
00386 update_rects.clear();
00387 if (fake_screen_) return 0;
00388 mode_changed_ = true;
00389
00390 flags = get_flags(flags);
00391 const int res = SDL_VideoModeOK( x, y, bits_per_pixel, flags );
00392
00393 if( res == 0 )
00394 return 0;
00395
00396 fullScreen = (flags & FULL_SCREEN) != 0;
00397 frameBuffer = SDL_SetVideoMode( x, y, bits_per_pixel, flags );
00398
00399 if( frameBuffer != NULL ) {
00400 image::set_pixel_format(frameBuffer->format);
00401 return bits_per_pixel;
00402 } else return 0;
00403 }
00404
00405 bool CVideo::modeChanged()
00406 {
00407 bool ret = mode_changed_;
00408 mode_changed_ = false;
00409 return ret;
00410 }
00411
00412 int CVideo::getx() const
00413 {
00414 return frameBuffer->w;
00415 }
00416
00417 int CVideo::gety() const
00418 {
00419 return frameBuffer->h;
00420 }
00421
00422 void CVideo::flip()
00423 {
00424 if(fake_screen_)
00425 return;
00426
00427 if(update_all) {
00428 ::SDL_Flip(frameBuffer);
00429 } else if(update_rects.empty() == false) {
00430 calc_rects();
00431 if(!update_rects.empty()) {
00432 SDL_UpdateRects(frameBuffer, update_rects.size(), &update_rects[0]);
00433 }
00434 }
00435
00436 clear_updates();
00437 }
00438
00439 void CVideo::lock_updates(bool value)
00440 {
00441 if(value == true)
00442 ++updatesLocked_;
00443 else
00444 --updatesLocked_;
00445 }
00446
00447 bool CVideo::update_locked() const
00448 {
00449 return updatesLocked_ > 0;
00450 }
00451
00452 surface& CVideo::getSurface()
00453 {
00454 return frameBuffer;
00455 }
00456
00457 bool CVideo::isFullScreen() const { return fullScreen; }
00458
00459 void CVideo::setBpp( int bpp )
00460 {
00461 bpp_ = bpp;
00462 }
00463
00464 int CVideo::getBpp()
00465 {
00466 return bpp_;
00467 }
00468
00469 int CVideo::set_help_string(const std::string& str)
00470 {
00471 font::remove_floating_label(help_string_);
00472
00473 const SDL_Color color = { 0, 0, 0, 0xbb };
00474
00475 int size = font::SIZE_LARGE;
00476
00477 while(size > 0) {
00478 if(font::line_width(str, size) > getx()) {
00479 size--;
00480 } else {
00481 break;
00482 }
00483 }
00484
00485 const int border = 5;
00486
00487 font::floating_label flabel(str);
00488 flabel.set_font_size(size);
00489 flabel.set_position(getx()/2, gety());
00490 flabel.set_bg_color(color);
00491 flabel.set_border_size(border);
00492
00493 help_string_ = font::add_floating_label(flabel);
00494
00495 const SDL_Rect& rect = font::get_floating_label_rect(help_string_);
00496 font::move_floating_label(help_string_,0.0,-double(rect.h));
00497 return help_string_;
00498 }
00499
00500 void CVideo::clear_help_string(int handle)
00501 {
00502 if(handle == help_string_) {
00503 font::remove_floating_label(handle);
00504 help_string_ = 0;
00505 }
00506 }
00507
00508 void CVideo::clear_all_help_strings()
00509 {
00510 clear_help_string(help_string_);
00511 }