1
0
forked from 0ad/0ad

Adds hardware cursors for Linux and OS X. Requires libxcursor on Linux. Fixes #748.

Adds explicit links to frameworks we need on OS X.

This was SVN commit r11596.
This commit is contained in:
historic_bruno 2012-04-21 07:53:53 +00:00
parent d3d95c6b66
commit b101f5ad01
7 changed files with 324 additions and 76 deletions

View File

@ -587,6 +587,13 @@ extern_lib_defs = {
})
end,
},
xcursor = {
link_settings = function()
add_default_links({
unix_names = { "Xcursor" },
})
end,
},
zlib = {
compile_settings = function()
if os.is("windows") then

View File

@ -707,8 +707,9 @@ used_extern_libs = {
"valgrind",
}
if not os.is("windows") and not _OPTIONS["android"] then
if not os.is("windows") and not _OPTIONS["android"] and not os.is("macosx") then
table.insert(used_extern_libs, "x11")
table.insert(used_extern_libs, "xcursor")
end
if not _OPTIONS["without-audio"] then
@ -760,7 +761,7 @@ function setup_main_exe ()
elseif os.is("linux") or os.is("bsd") then
if not _OPTIONS["without-fam"] then
if not _OPTIONS["without-fam"] then
links { "fam" }
end
@ -797,7 +798,10 @@ function setup_main_exe ()
configuration { }
elseif os.is("macosx") then
links { "pthread" }
linkoptions { "-framework ApplicationServices", "-framework Cocoa", "-framework CoreFoundation" }
end
end

View File

@ -39,7 +39,7 @@
// On Windows, allow runtime choice between system cursors and OpenGL
// cursors (Windows = more responsive, OpenGL = more consistent with what
// the game sees)
#if OS_WIN
#if OS_WIN || OS_UNIX
# define ALLOW_SYS_CURSOR 1
#else
# define ALLOW_SYS_CURSOR 0

View File

@ -23,6 +23,9 @@
#include "precompiled.h"
#include "lib/sysdep/sysdep.h"
#include "lib/sysdep/cursor.h"
#include "lib/external_libraries/libsdl.h"
Status sys_clipboard_set(const wchar_t* UNUSED(text))
{
@ -61,3 +64,57 @@ Status GetVideoMode(int* xres, int* yres, int* bpp, int* freq)
}
}
// stub implementation of sys_cursor* functions
// note: do not return ERR_NOT_IMPLEMENTED or similar because that
// would result in WARN_ERRs.
Status sys_cursor_create(int w, int h, void* bgra_img, int hx, int hy, sys_cursor* cursor)
{
UNUSED2(w);
UNUSED2(h);
UNUSED2(hx);
UNUSED2(hy);
UNUSED2(bgra_img);
*cursor = 0;
return INFO::OK;
}
// returns a dummy value representing an empty cursor
Status sys_cursor_create_empty(sys_cursor* cursor)
{
*cursor = (void*)1; // any non-zero value, since the cursor NULL has special meaning
return INFO::OK;
}
// replaces the current system cursor with the one indicated. need only be
// called once per cursor; pass 0 to restore the default.
Status sys_cursor_set(sys_cursor cursor)
{
if (cursor) // dummy empty cursor
SDL_ShowCursor(SDL_DISABLE);
else // restore default cursor
SDL_ShowCursor(SDL_ENABLE);
return INFO::OK;
}
// destroys the indicated cursor and frees its resources. if it is
// currently the system cursor, the default cursor is restored first.
Status sys_cursor_free(sys_cursor cursor)
{
// bail now to prevent potential confusion below; there's nothing to do.
if(!cursor)
return INFO::OK;
SDL_ShowCursor(SDL_ENABLE);
return INFO::OK;
}
Status sys_cursor_reset()
{
return INFO::OK;
}

View File

@ -0,0 +1,104 @@
/* Copyright (c) 2012 Wildfire Games
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import "precompiled.h"
#import "lib/sysdep/cursor.h"
#import <AppKit/NSCursor.h>
#import <AppKit/NSImage.h>
#import <ApplicationServices/ApplicationServices.h>
//TODO: make sure these are threadsafe
Status sys_cursor_create(int w, int h, void* bgra_img, int hx, int hy, sys_cursor* cursor)
{
NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:0 pixelsWide:w pixelsHigh:h
bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:w*4 bitsPerPixel:0];
if (!bitmap)
{
debug_printf(L"sys_cursor_create: Error creating NSBitmapImageRep!\n");
return ERR::FAIL;
}
u8* planes[5];
[bitmap getBitmapDataPlanes:planes];
const u8* bgra = static_cast<const u8*>(bgra_img);
u8* dst = planes[0];
for (int i = 0; i < w*h*4; i += 4)
{
dst[i] = bgra[i+2];
dst[i+1] = bgra[i+1];
dst[i+2] = bgra[i];
dst[i+3] = bgra[i+3];
}
NSImage* image = [[NSImage alloc] init];
if (!image)
{
[bitmap release];
debug_printf(L"sys_cursor_create: Error creating NSImage!\n");
return ERR::FAIL;
}
[image addRepresentation:bitmap];
[bitmap release];
NSCursor* impl = [[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint(hx, hy)];
[image release];
if (!impl)
{
debug_printf(L"sys_cursor_create: Error creating NSCursor!\n");
return ERR::FAIL;
}
*cursor = static_cast<sys_cursor>(impl);
return INFO::OK;
}
Status sys_cursor_free(sys_cursor cursor)
{
NSCursor* impl = static_cast<NSCursor*>(cursor);
[impl release];
return INFO::OK;
}
Status sys_cursor_create_empty(sys_cursor* cursor)
{
static u8 empty_bgra[] = {0, 0, 0, 0};
sys_cursor_create(1, 1, reinterpret_cast<void*>(empty_bgra), 0, 0, cursor);
return INFO::OK;
}
Status sys_cursor_set(sys_cursor cursor)
{
NSCursor* impl = static_cast<NSCursor*>(cursor);
[impl set];
return INFO::OK;
}
Status sys_cursor_reset()
{
return INFO::OK;
}

View File

@ -26,10 +26,8 @@
#include <stdio.h>
#include <wchar.h>
#include "lib/external_libraries/libsdl.h"
#include "lib/utf8.h"
#include "lib/sysdep/sysdep.h"
#include "lib/sysdep/cursor.h"
#include "udbg.h"
#include <boost/algorithm/string/replace.hpp>
@ -45,7 +43,6 @@
#define URL_OPEN_COMMAND "xdg-open"
#endif
bool sys_IsDebuggerPresent()
{
return false;
@ -283,60 +280,6 @@ Status sys_StatusDescription(int err, wchar_t* buf, size_t max_chars)
return ERR::FAIL;
}
// stub for sys_cursor_create - we don't need to implement this (SDL/X11 only
// has monochrome cursors so we need to use the software cursor anyways)
// note: do not return ERR_NOT_IMPLEMENTED or similar because that
// would result in WARN_ERRs.
Status sys_cursor_create(size_t w, size_t h, void* bgra_img, size_t hx, size_t hy, sys_cursor* cursor)
{
UNUSED2(w);
UNUSED2(h);
UNUSED2(hx);
UNUSED2(hy);
UNUSED2(bgra_img);
*cursor = 0;
return INFO::OK;
}
// returns a dummy value representing an empty cursor
Status sys_cursor_create_empty(sys_cursor* cursor)
{
*cursor = (void*)1; // any non-zero value, since the cursor NULL has special meaning
return INFO::OK;
}
// replaces the current system cursor with the one indicated. need only be
// called once per cursor; pass 0 to restore the default.
Status sys_cursor_set(sys_cursor cursor)
{
if (cursor) // dummy empty cursor
SDL_ShowCursor(SDL_DISABLE);
else // restore default cursor
SDL_ShowCursor(SDL_ENABLE);
return INFO::OK;
}
// destroys the indicated cursor and frees its resources. if it is
// currently the system cursor, the default cursor is restored first.
Status sys_cursor_free(sys_cursor cursor)
{
// bail now to prevent potential confusion below; there's nothing to do.
if(!cursor)
return INFO::OK;
SDL_ShowCursor(SDL_ENABLE);
return INFO::OK;
}
Status sys_cursor_reset()
{
return INFO::OK;
}
// note: just use the sector size: Linux aio doesn't really care about
// the alignment of buffers/lengths/offsets, so we'll just pick a
// sane value and not bother scanning all drives.

View File

@ -34,6 +34,7 @@
#include "lib/debug.h"
#include "lib/sysdep/gfx.h"
#include "lib/sysdep/cursor.h"
#include "ps/VideoMode.h"
@ -42,6 +43,7 @@
#include <Xlib.h>
#include <stdlib.h>
#include <Xatom.h>
#include <Xcursor/Xcursor.h>
#include "SDL.h"
#include "SDL_syswm.h"
@ -147,7 +149,7 @@ Expansions:
wchar_t *sys_clipboard_get()
{
Display *disp=XOpenDisplay(NULL);
if (!disp)
if(!disp)
return NULL;
// We use CLIPBOARD as the default, since the CLIPBOARD semantics are much
@ -155,7 +157,7 @@ wchar_t *sys_clipboard_get()
Atom selSource=XInternAtom(disp, "CLIPBOARD", False);
Window selOwner=XGetSelectionOwner(disp, selSource);
if (selOwner == None)
if(selOwner == None)
{
// However, since many apps don't use CLIPBOARD, but use PRIMARY instead
// we use XA_PRIMARY as a fallback clipboard. This is true for xterm,
@ -163,7 +165,7 @@ wchar_t *sys_clipboard_get()
selSource=XA_PRIMARY;
selOwner=XGetSelectionOwner(disp, selSource);
}
if (selOwner != None) {
if(selOwner != None) {
Atom pty=XInternAtom(disp, "SelectionPropertyTemp", False);
XConvertSelection(disp, selSource, XA_STRING, pty, selOwner, CurrentTime);
XFlush(disp);
@ -171,7 +173,7 @@ wchar_t *sys_clipboard_get()
Atom type;
int format=0, result=0;
unsigned long len=0, bytes_left=0, dummy=0;
unsigned char *data=NULL;
u8 *data=NULL;
// Get the length of the property and some attributes
// bytes_left will contain the length of the selection
@ -183,22 +185,22 @@ wchar_t *sys_clipboard_get()
&format, // return format
&len, &bytes_left,
&data);
if (result != Success)
if(result != Success)
debug_printf(L"clipboard_get: result: %d type:%lu len:%lu format:%d bytes_left:%lu\n",
result, type, len, format, bytes_left);
if (result == Success && bytes_left > 0)
if(result == Success && bytes_left > 0)
{
result = XGetWindowProperty (disp, selOwner,
pty, 0, bytes_left, 0,
AnyPropertyType, &type, &format,
&len, &dummy, &data);
if (result == Success)
if(result == Success)
{
debug_printf(L"clipboard_get: XGetWindowProperty succeeded, returning data\n");
debug_printf(L"clipboard_get: data was: \"%hs\", type was %lu, XA_STRING atom is %lu\n", data, type, XA_STRING);
if (type == XA_STRING) //Latin-1: Just copy into low byte of wchar_t
if(type == XA_STRING) //Latin-1: Just copy into low byte of wchar_t
{
wchar_t *ret=(wchar_t *)malloc((bytes_left+1)*sizeof(wchar_t));
std::copy(data, data+bytes_left, ret);
@ -239,7 +241,7 @@ int clipboard_filter(const SDL_Event* event)
{
/* Pass on all non-window manager specific events immediately */
/* And do nothing if we don't actually have a clip-out to send out */
if (event->type != SDL_SYSWMEVENT || !selection_data)
if(event->type != SDL_SYSWMEVENT || !selection_data)
return 1;
/* Handle window-manager specific clipboard events */
@ -250,7 +252,7 @@ int clipboard_filter(const SDL_Event* event)
#else
XEvent* xevent = &event->syswm.msg->event.xevent;
#endif
switch (xevent->type) {
switch(xevent->type) {
/* Copy the selection from our buffer to the requested property, and
convert to the requested target format */
case SelectionRequest: {
@ -267,12 +269,12 @@ int clipboard_filter(const SDL_Event* event)
sevent.xselection.time = req->time;
// Simply strip all non-Latin1 characters and replace with '?'
// We should support XA_UTF8
if (req->target == XA_STRING)
if(req->target == XA_STRING)
{
size_t size = wcslen(selection_data);
u8* buf = (u8*)alloca(size);
for (size_t i = 0; i < size; i++)
for(size_t i = 0; i < size; i++)
{
buf[i] = selection_data[i] < 0x100 ? selection_data[i] : '?';
}
@ -302,13 +304,13 @@ Status x11_clipboard_init()
SDL_VERSION(&info.version);
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (SDL_GetWindowWMInfo(g_VideoMode.GetWindow(), &info))
if(SDL_GetWindowWMInfo(g_VideoMode.GetWindow(), &info))
#else
if (SDL_GetWMInfo(&info))
if(SDL_GetWMInfo(&info))
#endif
{
/* Save the information for later use */
if (info.subsystem == SDL_SYSWM_X11)
if(info.subsystem == SDL_SYSWM_X11)
{
g_SDL_Display = info.info.x11.display;
g_SDL_Window = info.info.x11.window;
@ -352,7 +354,7 @@ Status sys_clipboard_set(const wchar_t *str)
debug_printf(L"sys_clipboard_set: %ls\n", str);
if (selection_data)
if(selection_data)
{
free(selection_data);
selection_data = NULL;
@ -383,4 +385,135 @@ Status sys_clipboard_set(const wchar_t *str)
return INFO::OK;
}
struct sys_cursor_impl
{
XcursorImage* image;
X__Cursor cursor;
};
static XcursorPixel cursor_pixel_to_x11_format(const XcursorPixel& bgra_pixel)
{
BOOST_STATIC_ASSERT(sizeof(XcursorPixel) == 4 * sizeof(u8));
XcursorPixel ret;
u8* dst = reinterpret_cast<u8*>(&ret);
const u8* b = reinterpret_cast<const u8*>(&bgra_pixel);
const u8 a = b[3];
for(size_t i = 0; i < 3; ++i)
*dst++ = (b[i]) * a / 255;
*dst = a;
return ret;
}
static bool get_wminfo(SDL_SysWMinfo& wminfo)
{
SDL_VERSION(&wminfo.version);
const int ret = SDL_GetWMInfo(&wminfo);
if(ret == 1)
return true;
if(ret == -1)
{
debug_printf(L"SDL_GetWMInfo failed\n");
return false;
}
if(ret == 0)
{
debug_printf(L"SDL_GetWMInfo is not implemented on this platform\n");
return false;
}
debug_printf(L"SDL_GetWMInfo returned an unknown value: %d\n", ret);
return false;
}
Status sys_cursor_create(int w, int h, void* bgra_img, int hx, int hy, sys_cursor* cursor)
{
debug_printf(L"Using Xcursor to sys_cursor_create %d x %d cursor\n", w, h);
XcursorImage* image = XcursorImageCreate(w, h);
if(!image)
WARN_RETURN(ERR::FAIL);
const XcursorPixel* bgra_img_begin = reinterpret_cast<XcursorPixel*>(bgra_img);
std::transform(bgra_img_begin, bgra_img_begin + (w*h), image->pixels,
cursor_pixel_to_x11_format);
image->xhot = hx;
image->yhot = hy;
SDL_SysWMinfo wminfo;
if(!get_wminfo(wminfo))
WARN_RETURN(ERR::FAIL);
sys_cursor_impl* impl = new sys_cursor_impl;
impl->image = image;
impl->cursor = XcursorImageLoadCursor(wminfo.info.x11.display, image);
if(impl->cursor == None)
WARN_RETURN(ERR::FAIL);
*cursor = static_cast<sys_cursor>(impl);
return INFO::OK;
}
// returns a dummy value representing an empty cursor
Status sys_cursor_create_empty(sys_cursor* cursor)
{
static u8 transparent_bgra[] = { 0x0, 0x0, 0x0, 0x0 };
return sys_cursor_create(1, 1, static_cast<void*>(transparent_bgra), 0, 0, cursor);
}
// replaces the current system cursor with the one indicated. need only be
// called once per cursor; pass 0 to restore the default.
Status sys_cursor_set(sys_cursor cursor)
{
if(!cursor) // restore default cursor
SDL_ShowCursor(SDL_DISABLE);
else
{
SDL_SysWMinfo wminfo;
if(!get_wminfo(wminfo))
WARN_RETURN(ERR::FAIL);
wminfo.info.x11.lock_func();
SDL_ShowCursor(SDL_ENABLE);
// wminfo.info.x11.window is sometimes 0, in which case
// it causes a crash; in these cases use fswindow instead
Window& window = wminfo.info.x11.window ? wminfo.info.x11.window : wminfo.info.x11.fswindow;
XDefineCursor(wminfo.info.x11.display, window,
static_cast<sys_cursor_impl*>(cursor)->cursor);
wminfo.info.x11.unlock_func();
}
return INFO::OK;
}
// destroys the indicated cursor and frees its resources. if it is
// currently the system cursor, the default cursor is restored first.
Status sys_cursor_free(sys_cursor cursor)
{
// bail now to prevent potential confusion below; there's nothing to do.
if(!cursor)
return INFO::OK;
sys_cursor_set(0); // restore default cursor
sys_cursor_impl* impl = static_cast<sys_cursor_impl*>(cursor);
XcursorImageDestroy(impl->image);
SDL_SysWMinfo wminfo;
if(!get_wminfo(wminfo))
return ERR::FAIL;
XFreeCursor(wminfo.info.x11.display, impl->cursor);
delete impl;
return INFO::OK;
}
Status sys_cursor_reset()
{
return INFO::OK;
}
#endif // #if HAVE_X