Hi All,<br><br>I finally had a bit of time to gather the changes to the spice client in order to get it working under Mac.<br>Changes:<br>- initializing variables so the compiler doesn't complain<br>- fixed socket flags (MSG_NOSIGNAL => SO_NOSIGPIPE)<br>
- implemented missing pthread_mutex_timedlock()<br>- replaced clock_gettime with gettimeofday<br>- added some different headers<br>- replaced the event handler with a select() based one instead of epoll<br>- pthread_yield() replaced with pthread_yield_np() for OS X<br>
- disabled clipboard (XConvertSelection), it seem to break things right now<br>- added initial audio playback using PortAudio (no recording yet, sorry!) + configure changes to detect OS X and not check for alsa<br>- force X11 keycodes for OSX instead of evdev<br>
<br>I'm not saying its beautiful, but it does the job :)<br><br>- Attila<br><br><br> client/application.cpp | 2 +-<br> client/client_net_socket.cpp | 7 ++<br> client/cmd_line_parser.cpp | 8 ++-<br>
client/threads.cpp | 40 +++++++++<br> client/x11/Makefile.am | 4 +-<br> client/x11/atomic_count.h | 23 +++--<br> client/x11/event_sources_p.cpp | 188 ++++++++++++++++++++--------------------<br>
client/x11/event_sources_p.h | 42 ++++++---<br> client/x11/named_pipe.cpp | 2 +-<br> client/x11/platform.cpp | 33 +++++++-<br> client/x11/playback.cpp | 80 ++++++++++++++++-<br> client/x11/playback.h | 11 +++<br>
client/x11/record.cpp | 14 ++-<br> client/x11/record.h | 4 +<br> client/x11/red_window.cpp | 4 +-<br> common/lines.c | 1 +<br> <a href="http://configure.ac">configure.ac</a> | 31 +++++--<br>
17 files changed, 353 insertions(+), 141 deletions(-)<br><br>diff --git a/client/application.cpp b/client/application.cpp<br>index 490cd8c..7ebeea2 100644<br>--- a/client/application.cpp<br>+++ b/client/application.cpp<br>
@@ -648,7 +648,7 @@ RedScreen* Application::get_screen(int id)<br> if (!(screen = _screens[id])) {<br> Monitor* mon = find_monitor(id);<br> SpicePoint size;<br>-<br>+ size.x = size.y = 0;<br> if (_full_screen && mon) {<br>
size = mon->get_size();<br> } else {<br>diff --git a/client/client_net_socket.cpp b/client/client_net_socket.cpp<br>index 6ce44c5..014d5a6 100644<br>--- a/client/client_net_socket.cpp<br>+++ b/client/client_net_socket.cpp<br>
@@ -25,6 +25,13 @@<br> #include <spice/error_codes.h><br> #include "utils.h"<br> <br>+#if defined(__APPLE__) || defined(__MACH__)<br>+#ifndef MSG_NOSIGNAL<br>+#define MSG_NOSIGNAL SO_NOSIGPIPE<br>+#endif<br>
+#endif<br>+<br>+<br> ClientNetSocket::ClientNetSocket(uint16_t id, const struct in_addr& dst_addr, uint16_t dst_port,<br> ProcessLoop& process_loop, EventHandler& event_handler)<br>
: _id (id)<br>diff --git a/client/cmd_line_parser.cpp b/client/cmd_line_parser.cpp<br>index ef72dba..ba0bd5b 100644<br>--- a/client/cmd_line_parser.cpp<br>+++ b/client/cmd_line_parser.cpp<br>@@ -26,6 +26,12 @@<br> <br>
#define DISABLE_ABBREVIATE<br> <br>+#if defined(__APPLE__) || defined(__MACH__)<br>+#ifndef MSG_NOSIGNAL<br>+#define MSG_NOSIGNAL SO_NOSIGPIPE<br>+#endif<br>+#endif<br>+<br> <br> CmdLineParser::Option::Option(int in_id, const std::string& in_name, char in_short_name,<br>
OptionType in_type, const std::string& in_help,<br>@@ -450,7 +456,7 @@ void CmdLineParser::show_help()<br> static const int HELP_WIDTH = 80 - HELP_START_POS;<br> std::ostringstream os;<br>
<br>- os << basename(_argv[0]) << " - " << _description.c_str() << "\n\noptions:\n\n";<br>+ os << _argv[0] << " - " << _description.c_str() << "\n\noptions:\n\n";<br>
<br> Options::iterator iter = _options.begin();<br> for (; iter != _options.end(); ++iter) {<br>diff --git a/client/threads.cpp b/client/threads.cpp<br>index a9b8ea5..098134a 100644<br>--- a/client/threads.cpp<br>
+++ b/client/threads.cpp<br>@@ -21,6 +21,38 @@<br> #include "debug.h"<br> #ifdef WIN32<br> #include <sys/timeb.h><br>+#elif defined(__MACH__) || defined(__APPLE__)<br>+#include <sys/time.h><br>+#define _x_min(a, b) ((a) < (b) ? (a) : (b))<br>
+#if _POSIX_TIMERS <= 0<br>+// FIXME: workaround to missing pthread_mutex_timedlock in Mac OS X<br>+int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abs_timeout)<br>+{<br>+ int pthread_rc;<br>
+ struct timespec remaining, slept, ts;<br>+<br>+ remaining = *abs_timeout;<br>+ while ((pthread_rc = pthread_mutex_trylock(mutex)) == EBUSY) {<br>+ ts.tv_sec = 0;<br>+ ts.tv_nsec = (remaining.tv_sec > 0 ? 10000000 : _x_min(remaining.tv_nsec,10000000));<br>
+ nanosleep(&ts, &slept);<br>+ ts.tv_nsec -= slept.tv_nsec;<br>+ if (ts.tv_nsec <= remaining.tv_nsec) {<br>+ remaining.tv_nsec -= ts.tv_nsec;<br>+ }<br>+ else {<br>
+ remaining.tv_sec--;<br>+ remaining.tv_nsec = (1000000 - (ts.tv_nsec - remaining.tv_nsec));<br>+ }<br>+ if (remaining.tv_sec < 0 || (!remaining.tv_sec && remaining.tv_nsec <= 0)) {<br>
+ return ETIMEDOUT;<br>+ }<br>+ }<br>+<br>+ return pthread_rc;<br>+}<br>+<br>+#endif<br> #endif<br> <br> Thread::Thread(thread_main_t thread_main, void* opaque)<br>@@ -43,6 +75,11 @@ static inline void rel_time(struct timespec& time, uint64_t delta_nano)<br>
_ftime_s(&now);<br> time.tv_sec = (long)now.time;<br> time.tv_nsec = now.millitm * 1000 * 1000;<br>+#elif defined(__APPLE__) || defined(__MACH__)<br>+ struct timeval tv;<br>+ gettimeofday(&tv,NULL);<br>
+ time.tv_sec = tv.tv_sec;<br>+ time.tv_nsec = tv.tv_usec*1000;<br> #else<br> clock_gettime(CLOCK_MONOTONIC, &time);<br> #endif<br>@@ -75,10 +112,13 @@ Condition::Condition()<br> #else<br> pthread_condattr_t attr;<br>
pthread_condattr_init(&attr);<br>+#if defined(__MACH__) || defined(__APPLE__)<br>+#else<br> int r;<br> if ((r = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC))) {<br> THROW("set clock failed %d", r);<br>
}<br>+#endif<br> pthread_cond_init(&_condition, &attr);<br> pthread_condattr_destroy(&attr);<br> #endif<br>diff --git a/client/x11/Makefile.am b/client/x11/Makefile.am<br>index 101f6dd..ceb07ef 100644<br>
--- a/client/x11/Makefile.am<br>+++ b/client/x11/Makefile.am<br>@@ -200,8 +200,10 @@ spicec_LDFLAGS = \<br> spicec_LDADD = \<br> $(PIXMAN_LIBS) \<br> $(ALSA_LIBS) \<br>
+ $(PA_LIBS) \<br> $(GL_LIBS) \<br> $(XRANDR_LIBS) \<br> $(MISC_X_LIBS) \<br> $(CEGUI_LIBS) \<br>- -lrt<br>
+ $(LIBRT)<br>+<br>diff --git a/client/x11/atomic_count.h b/client/x11/atomic_count.h<br>index f48c667..8e6fd15 100644<br>--- a/client/x11/atomic_count.h<br>+++ b/client/x11/atomic_count.h<br>@@ -1,23 +1,28 @@<br> /*<br>
Copyright (C) 2009 Red Hat, Inc.<br> <br>- This library is free software; you can redistribute it and/or<br>- modify it under the terms of the GNU Lesser General Public<br>- License as published by the Free Software Foundation; either<br>
- version 2.1 of the License, or (at your option) any later version.<br>+ This program is free software; you can redistribute it and/or<br>+ modify it under the terms of the GNU General Public License as<br>+ published by the Free Software Foundation; either version 2 of<br>
+ the License, or (at your option) any later version.<br> <br>- This library is distributed in the hope that it will be useful,<br>+ This program is distributed in the hope that it will be useful,<br> but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br>- Lesser General Public License for more details.<br>+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>+ GNU General Public License for more details.<br>
<br>- You should have received a copy of the GNU Lesser General Public<br>- License along with this library; if not, see <<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>.<br>+ You should have received a copy of the GNU General Public License<br>
+ along with this program. If not, see <<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>.<br> */<br> <br> #ifndef _H_ATOMIC_COUNT<br> #define _H_ATOMIC_COUNT<br> <br>+#if defined(__MACH__) || defined(__APPLE__)<br>
+#include <stdint.h><br>+#endif<br>+<br>+<br> class AtomicCount {<br> public:<br> AtomicCount(uint32_t count = 0) : _count (count) {}<br>diff --git a/client/x11/event_sources_p.cpp b/client/x11/event_sources_p.cpp<br>
index d59bffd..c164500 100644<br>--- a/client/x11/event_sources_p.cpp<br>+++ b/client/x11/event_sources_p.cpp<br>@@ -1,40 +1,29 @@<br> /*<br> Copyright (C) 2009 Red Hat, Inc.<br> <br>- This library is free software; you can redistribute it and/or<br>
- modify it under the terms of the GNU Lesser General Public<br>- License as published by the Free Software Foundation; either<br>- version 2.1 of the License, or (at your option) any later version.<br>+ This program is free software; you can redistribute it and/or<br>
+ modify it under the terms of the GNU General Public License as<br>+ published by the Free Software Foundation; either version 2 of<br>+ the License, or (at your option) any later version.<br> <br>- This library is distributed in the hope that it will be useful,<br>
+ This program is distributed in the hope that it will be useful,<br> but WITHOUT ANY WARRANTY; without even the implied warranty of<br>- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br>- Lesser General Public License for more details.<br>
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>+ GNU General Public License for more details.<br> <br>- You should have received a copy of the GNU Lesser General Public<br>- License along with this library; if not, see <<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>.<br>
+ You should have received a copy of the GNU General Public License<br>+ along with this program. If not, see <<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>.<br> */<br>-<br>-#include <sys/epoll.h><br>
#include <sys/fcntl.h><br>-<br> #include "event_sources.h"<br>-#include "debug.h"<br>-#include "utils.h"<br>-<br>-#ifdef USING_EVENT_FD<br>-#include <sys/eventfd.h><br>-#endif<br>
<br>-#define NUM_EPOLL_EVENTS 10<br>+#include <sys/select.h><br> <br>-#ifdef USING_EVENT_FD<br>-#define WRITE_FD _event_fd<br>-#define EVENT_DATA_TYPE eventfd_t<br>-#else<br> #define WRITE_FD _event_write_fd<br> #define EVENT_DATA_TYPE uint8_t<br>
-#endif<br>+<br>+#include "debug.h"<br>+#include "utils.h"<br> <br> class EventWrapper {<br> public:<br>@@ -77,9 +66,12 @@ private:<br> <br> EventSources::EventSources()<br> {<br>- _epoll = epoll_create(NUM_EPOLL_EVENTS);<br>
- if (_epoll == -1) {<br>- THROW("create epool failed");<br>+ FD_ZERO(&_readfds);<br>+ FD_ZERO(&_writefds);<br>+ for(int i=0;i<NUM_EVENTS;i++) {<br>+ _lofds[i].fd = 0;<br>+ _lofds[i].fd = NULL;<br>
+ _lofds[i].rw = 0;<br> }<br> }<br> <br>@@ -89,48 +81,82 @@ EventSources::~EventSources()<br> for (; iter != _events.end(); iter++) {<br> delete *iter;<br> }<br>- close(_epoll);<br> }<br> <br>-bool EventSources::wait_events(int timeout_ms)<br>
+bool EventSources::wait_events(int timeout_milli)<br> {<br>- struct epoll_event events[NUM_EPOLL_EVENTS];<br>- int num_events = epoll_wait(_epoll, events, NUM_EPOLL_EVENTS, timeout_ms);<br>+ int num_events = 0;<br>
+ struct timeval tmout;<br>+ tmout.tv_sec = 0;<br>+ tmout.tv_usec = timeout_milli*1000;<br>+ fd_set tmpreadfds, tmpwritefds; <br>+<br>+ tmpreadfds = _readfds;<br>+ tmpwritefds = _writefds;<br> <br>
+ if (timeout_milli == -1) { <br>+ num_events = select(_hfd+1, &tmpreadfds, &tmpwritefds, NULL, NULL);<br>+ } else {<br>+ num_events = select(_hfd+1, &tmpreadfds, &tmpwritefds, NULL, &tmout);<br>
+ }<br> if (num_events == -1) {<br>- if (errno == EINTR) {<br>- return false;<br>- }<br>- THROW("wait error eventfd failed");<br>+ //THROW("select() failed");<br>
+ return false;<br>+ } else if (num_events == 0) {<br>+ return false;<br>+ } else {<br>+ for (int i = 0; i < NUM_EVENTS; i++) {<br>+ if ((_lofds[i].rw && FD_ISSET(_lofds[i].fd, &tmpwritefds)) ||<br>
+ FD_ISSET(_lofds[i].fd, &tmpreadfds)) {<br>+ EventWrapper* wrapper;<br>+ EventSource* event;<br>+ wrapper = (EventWrapper *)_lofds[i].wrapper;<br>+ wrapper->ref();<br>
+ if ((event = wrapper->get_event())) {<br>+ event->action();<br>+ }<br>+ wrapper->unref();<br>+ }<br>+ }<br> }<br>+ return false;<br>+}<br> <br>- for (int i = 0; i < num_events; i++) {<br>
- ((EventWrapper*)events[i].data.ptr)->ref();<br>+void EventSources_p::add_fd(int fd,EventWrapper *wrapper, unsigned char rw) { <br>+ // save the highest fd<br>+ if (fd > _hfd)<br>+ _hfd = fd;<br>+ for(int i=0;i<NUM_EVENTS;i++) {<br>
+ if (_lofds[i].fd == 0) {<br>+ _lofds[i].fd = fd;<br>+ _lofds[i].wrapper = wrapper;<br>+ _lofds[i].rw = rw;<br>+ FD_SET(fd, &_readfds);<br>+ if (rw)<br>+ FD_SET(fd, &_writefds);<br>
+ break;<br>+ }<br> }<br>+}<br> <br>- for (int i = 0; i < num_events; i++) {<br>- EventWrapper* wrapper;<br>- EventSource* event;<br>-<br>- wrapper = (EventWrapper *)events[i].data.ptr;<br>
- if ((event = wrapper->get_event())) {<br>- event->action();<br>- }<br>- wrapper->unref();<br>- }<br>- return false;<br>+void EventSources_p::del_fd(int fd) {<br>+ for(int i=0;i<NUM_EVENTS;i++) {<br>
+ if (_lofds[i].fd == fd) {<br>+ FD_CLR(fd, &_readfds);<br>+ if (_lofds[i].rw)<br>+ FD_CLR(fd, &_writefds); <br>+ _lofds[i].fd = 0;<br>+ _lofds[i].wrapper = NULL;<br>
+ _lofds[i].rw = 0;<br>+ }<br>+ }<br> }<br> <br> void EventSources::add_trigger(Trigger& trigger)<br> {<br> int fd = trigger.get_fd();<br> EventWrapper* wrapper = new EventWrapper(*this, trigger);<br>
- struct epoll_event event;<br>- event.data.ptr = wrapper;<br>- event.events = EPOLLIN;<br>- if (epoll_ctl(_epoll, EPOLL_CTL_ADD, fd, &event) == -1) {<br>- THROW("epoll add failed");<br>- }<br>
+ add_fd(fd, wrapper, 0);<br> _events.push_back(wrapper);<br> }<br> <br>@@ -162,26 +188,18 @@ void EventSources::remove_trigger(Trigger& trigger)<br> }<br> }<br> int fd = trigger.get_fd();<br>- if (epoll_ctl(_epoll, EPOLL_CTL_DEL, fd, NULL) == -1) {<br>
- THROW("epoll remove failed");<br>- }<br>+ del_fd(fd);<br> }<br> <br> EventSources::Trigger::Trigger()<br> {<br>-#ifdef USING_EVENT_FD<br>- _event_fd = eventfd(0, 0);<br>- if (_event_fd == -1) {<br>
- THROW("create eventfd failed");<br>- }<br>-#else<br> int fd[2];<br> if (pipe(fd) == -1) {<br> THROW("create pipe failed");<br> }<br> _event_fd = fd[0];<br> _event_write_fd = fd[1];<br>
-#endif<br>+ <br> int flags;<br> if ((flags = fcntl(_event_fd, F_GETFL)) == -1) {<br> THROW("failed to set eventfd non block: %s", strerror(errno));<br>@@ -195,9 +213,6 @@ EventSources::Trigger::Trigger()<br>
EventSources::Trigger::~Trigger()<br> {<br> close(_event_fd);<br>-#ifndef USING_EVENT_FD<br>- close(_event_write_fd);<br>-#endif<br> }<br> <br> void EventSources::Trigger::trigger()<br>@@ -263,25 +278,6 @@ static void set_blocking(int fd)<br>
}<br> }<br> <br>-static void add_to_poll(int fd, int epoll, EventWrapper* wrapper)<br>-{<br>- struct epoll_event event;<br>- event.data.ptr = wrapper;<br>- event.events = EPOLLIN | EPOLLOUT | EPOLLET;<br>- if (epoll_ctl(epoll, EPOLL_CTL_ADD, fd, &event) == -1) {<br>
- THROW("epoll add failed");<br>- }<br>-}<br>-<br>-void EventSources::add_socket(Socket& socket)<br>-{<br>- int fd = socket.get_socket();<br>- set_non_blocking(fd);<br>- EventWrapper* wrapper = new EventWrapper(*this, socket);<br>
- add_to_poll(fd, _epoll, wrapper);<br>- _events.push_back(wrapper);<br>-}<br>-<br> static bool remove_event(EventSources_p::Events& events, EventSource& event)<br> {<br> EventSources_p::Events::iterator iter = events.begin();<br>
@@ -296,6 +292,15 @@ static bool remove_event(EventSources_p::Events& events, EventSource& event)<br> }<br> }<br> }<br>+void EventSources::add_socket(Socket& socket)<br>+{<br>+ int fd = socket.get_socket();<br>
+ set_non_blocking(fd);<br>+ EventWrapper* wrapper = new EventWrapper(*this, socket);<br>+ add_fd(fd, wrapper, 0); // last parameter should be 1 to monitor for write availability?<br>+ _events.push_back(wrapper);<br>
+}<br>+<br> <br> void EventSources::remove_socket(Socket& socket)<br> {<br>@@ -303,9 +308,7 @@ void EventSources::remove_socket(Socket& socket)<br> THROW("socket not found");<br> }<br> int fd = socket.get_socket();<br>
- if (epoll_ctl(_epoll, EPOLL_CTL_DEL, fd, NULL) == -1) {<br>- THROW("epoll remove failed");<br>- }<br>+ del_fd(fd);<br> set_blocking(fd);<br> }<br> <br>@@ -314,7 +317,7 @@ void EventSources::add_file(File& file)<br>
int fd = file.get_fd();<br> set_non_blocking(fd);<br> EventWrapper* wrapper = new EventWrapper(*this, file);<br>- add_to_poll(fd, _epoll, wrapper);<br>+ add_fd(fd, wrapper, 0);<br> _events.push_back(wrapper);<br>
}<br> <br>@@ -324,9 +327,7 @@ void EventSources::remove_file(File& file)<br> THROW("file not found");<br> }<br> int fd = file.get_fd();<br>- if (epoll_ctl(_epoll, EPOLL_CTL_DEL, fd, NULL) == -1) {<br>
- THROW("epoll remove failed");<br>- }<br>+ del_fd(fd);<br> set_blocking(fd);<br> }<br> <br>@@ -337,3 +338,4 @@ void EventSources::add_handle(Handle& file)<br> void EventSources::remove_handle(Handle& file)<br>
{<br> }<br>+<br>diff --git a/client/x11/event_sources_p.h b/client/x11/event_sources_p.h<br>index 09703c0..027ac9e 100644<br>--- a/client/x11/event_sources_p.h<br>+++ b/client/x11/event_sources_p.h<br>@@ -1,22 +1,26 @@<br>
/*<br> Copyright (C) 2009 Red Hat, Inc.<br> <br>- This library is free software; you can redistribute it and/or<br>- modify it under the terms of the GNU Lesser General Public<br>- License as published by the Free Software Foundation; either<br>
- version 2.1 of the License, or (at your option) any later version.<br>+ This program is free software; you can redistribute it and/or<br>+ modify it under the terms of the GNU General Public License as<br>+ published by the Free Software Foundation; either version 2 of<br>
+ the License, or (at your option) any later version.<br> <br>- This library is distributed in the hope that it will be useful,<br>+ This program is distributed in the hope that it will be useful,<br> but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br>- Lesser General Public License for more details.<br>+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>+ GNU General Public License for more details.<br>
<br>- You should have received a copy of the GNU Lesser General Public<br>- License along with this library; if not, see <<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>.<br>+ You should have received a copy of the GNU General Public License<br>
+ along with this program. If not, see <<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>.<br> */<br> <br>-#ifndef _H_EVENT_SOURCES_P<br>-#define _H_EVENT_SOURCES_P<br>+#ifndef _H_EVENTS_LOOP_P<br>
+#define _H_EVENTS_LOOP_P<br>+<br>+#define NUM_EVENTS 10<br>+<br>+#include <sys/select.h><br> <br> #include "common.h"<br> #include "threads.h"<br>@@ -29,12 +33,24 @@<br> <br> class EventWrapper;<br>
<br>+struct fd_to_ptr {<br>+ int fd;<br>+ EventWrapper *wrapper; <br>+ unsigned char rw;<br>+};<br>+<br> class EventSources_p {<br> public:<br>+ void add_fd(int, EventWrapper*, unsigned char);<br>+ void del_fd(int);<br>
void remove_wrapper(EventWrapper*);<br> <br> public:<br>- int _epoll;<br>+ int _hfd;<br>+ fd_set _readfds;<br>+ fd_set _writefds;<br>+ struct fd_to_ptr _lofds[NUM_EVENTS];<br>+<br> typedef std::list<EventWrapper*> Events;<br>
Events _events;<br> <br>@@ -49,9 +65,7 @@ public:<br> <br> public:<br> int _event_fd;<br>-#ifndef USING_EVENT_FD<br> int _event_write_fd;<br>-#endif<br> bool _pending_int;<br> Mutex _lock;<br> };<br>diff --git a/client/x11/named_pipe.cpp b/client/x11/named_pipe.cpp<br>
index c6f38da..db3930b 100644<br>--- a/client/x11/named_pipe.cpp<br>+++ b/client/x11/named_pipe.cpp<br>@@ -17,7 +17,7 @@<br> <br> #include <sys/types.h><br> #include <sys/socket.h><br>-#include <sys/epoll.h><br>
+//#include <sys/epoll.h><br> #include <sys/un.h><br> #include "named_pipe.h"<br> #include "utils.h"<br>diff --git a/client/x11/platform.cpp b/client/x11/platform.cpp<br>index cc1502b..e8d89b0 100644<br>
--- a/client/x11/platform.cpp<br>+++ b/client/x11/platform.cpp<br>@@ -30,15 +30,13 @@<br> #include <X11/extensions/XShm.h><br> #include <unistd.h><br> #include <sys/socket.h><br>-#include <sys/epoll.h><br>
+//#include <sys/epoll.h><br> #include <sys/resource.h><br> #include <sys/types.h><br> #include <sys/syscall.h><br>-#include <sys/stat.h><br> #include <unistd.h><br> #include <fcntl.h><br>
#include <set><br>-#include <values.h><br> #include <signal.h><br> #include <config.h><br> #include <sys/shm.h><br>@@ -61,6 +59,18 @@<br> #define BOOL bool<br> #include "named_pipe.h"<br>
<br>+#if defined(__LINUX__)<br>+#include <values.h><br>+#elif defined(__APPLE__) || defined(__MACH__)<br>+#include <float.h><br>+#include <machine/limits.h><br>+#include <sys/time.h><br>+#include <sys/stat.h><br>
+#include <assert.h><br>+#define MAXINT INT_MAX<br>+#define MININT INT_MIN<br>+#endif<br>+<br> //#define X_DEBUG_SYNC(display) XSync(display, False)<br> #define X_DEBUG_SYNC(display)<br> #ifdef HAVE_XRANDR12<br>@@ -427,7 +437,14 @@ void Platform::send_quit_request()<br>
uint64_t Platform::get_monolithic_time()<br> {<br> struct timespec time_space;<br>+#if defined(__LINUX__)<br> clock_gettime(CLOCK_MONOTONIC, &time_space);<br>+#elif defined(__APPLE__) || defined(__MACH__)<br>
+ struct timeval tv;<br>+ gettimeofday(&tv, NULL);<br>+ time_space.tv_sec = tv.tv_sec;<br>+ time_space.tv_nsec = tv.tv_usec*1000;<br>+#endif<br> return uint64_t(time_space.tv_sec) * 1000 * 1000 * 1000 + uint64_t(time_space.tv_nsec);<br>
}<br> <br>@@ -463,7 +480,11 @@ void Platform::msleep(unsigned int millisec)<br> <br> void Platform::yield()<br> {<br>+#if defined(__APPLE__) || defined(__MACH__)<br>+ pthread_yield_np();<br>+#else<br> pthread_yield();<br>
+#endif<br> }<br> <br> void Platform::term_printf(const char* format, ...)<br>@@ -3016,8 +3037,14 @@ void Platform::set_clipboard_listener(ClipboardListener* listener)<br> return;<br> }<br> clipboard_listener = listener;<br>
+ <br>+ // FIXME: Tis seems broken on Mac OS X... <br>+ // Fails with the following error:<br>+ // 1285496484 ERROR [87445:18446744073709551615] x_error_handler: x error on display /tmp/launch-h1OMho/org.x:0 error BadWindow (invalid Window parameter) minor 0 request X_ConvertSelection <br>
+#if !defined(__APPLE__) && !defined(__MACH__) <br> XConvertSelection(x_display, XA_PRIMARY, utf8_atom, clipboard_prop,<br> platform_win, CurrentTime);<br>+#endif<br> }<br> <br> bool Platform::set_clipboard_data(uint32_t type, const uint8_t* data, int32_t size)<br>
diff --git a/client/x11/playback.cpp b/client/x11/playback.cpp<br>index be89a98..75f1bb0 100644<br>--- a/client/x11/playback.cpp<br>+++ b/client/x11/playback.cpp<br>@@ -22,9 +22,6 @@<br> #define REING_SIZE_MS 300<br> <br>
WavePlayer::WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels)<br>- : _pcm (NULL)<br>- , _hw_params (NULL)<br>- , _sw_params (NULL)<br> {<br> if (!init(sampels_per_sec, bits_per_sample, channels)) {<br>
cleanup();<br>@@ -34,6 +31,7 @@ WavePlayer::WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint3<br> <br> void WavePlayer::cleanup()<br> {<br>+#if defined(__LINUX__)<br> if (_pcm) {<br> snd_pcm_close(_pcm);<br>
}<br>@@ -45,6 +43,10 @@ void WavePlayer::cleanup()<br> if (_sw_params) {<br> snd_pcm_sw_params_free(_sw_params);<br> }<br>+#elif defined(__MACH__) || defined(__APPLE__)<br>+ Pa_Terminate();<br>+ _stopped = 1;<br>
+#endif<br> }<br> <br> WavePlayer::~WavePlayer()<br>@@ -57,6 +59,8 @@ bool WavePlayer::init(uint32_t sampels_per_sec,<br> uint32_t channels)<br> {<br> const int frame_size = WavePlaybackAbstract::FRAME_SIZE;<br>
+ _sampels_per_ms = sampels_per_sec / 1000;<br>+#if defined(__LINUX__)<br> const char* pcm_device = "default";<br> snd_pcm_format_t format;<br> int err;<br>@@ -71,7 +75,6 @@ bool WavePlayer::init(uint32_t sampels_per_sec,<br>
default:<br> return false;<br> }<br>- _sampels_per_ms = sampels_per_sec / 1000;<br> <br> if ((err = snd_pcm_open(&_pcm, pcm_device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {<br> LOG_ERROR("cannot open audio playback device %s %s", pcm_device, snd_strerror(err));<br>
@@ -174,12 +177,44 @@ bool WavePlayer::init(uint32_t sampels_per_sec,<br> LOG_ERROR("cannot prepare pcm device %s", snd_strerror(err));<br> return false;<br> }<br>-<br>+#elif defined(__MACH__) || defined(__APPLE__)<br>
+ _err = Pa_Initialize();<br>+ if (_err != paNoError) {<br>+ LOG_ERROR("PortAudio initialization failure!");<br>+ return false;<br>+ }<br>+ <br>+ _outputParameters.device = Pa_GetDefaultOutputDevice();<br>
+ if (_outputParameters.device == paNoDevice) {<br>+ LOG_ERROR("PortAudio: No dfault output device");<br>+ return false;<br>+ }<br>+ _outputParameters.channelCount = channels;<br>+ _outputParameters.hostApiSpecificStreamInfo = NULL;<br>
+ switch (bits_per_sample) {<br>+ case 8:<br>+ _outputParameters.sampleFormat = paInt8;<br>+ break;<br>+ case 16:<br>+ _outputParameters.sampleFormat = paInt16;<br>+ break;<br>+ default:<br>
+ return false;<br>+ }<br>+ _outputParameters.suggestedLatency = Pa_GetDeviceInfo(_outputParameters.device)->defaultLowOutputLatency;<br>+ _err = Pa_OpenStream(&_stream, NULL, &_outputParameters, sampels_per_sec, frame_size, paClipOff, NULL, NULL);<br>
+ if (_err != paNoError) {<br>+ DBG(0,"PortAudio: Error number: %d message: %s", _err, Pa_GetErrorText(_err));<br>+ return false;<br>+ } <br>+ DBG(0, "Initialized PortAudio: samples/sec: %d channels: %d bits/sample: %d",sampels_per_sec, channels, bits_per_sample);<br>
+#endif<br> return true;<br> }<br> <br> bool WavePlayer::write(uint8_t* frame)<br> {<br>+#if defined(__LINUX__)<br> snd_pcm_sframes_t ret = snd_pcm_writei(_pcm, frame, WavePlaybackAbstract::FRAME_SIZE);<br> if (ret < 0) {<br>
if (ret == -EAGAIN) {<br>@@ -190,13 +225,43 @@ bool WavePlayer::write(uint8_t* frame)<br> snd_pcm_writei(_pcm, frame, WavePlaybackAbstract::FRAME_SIZE);<br> }<br> }<br>+#elif defined(__MACH__) || defined(__APPLE__)<br>
+ if (_stopped) {<br>+ _err = Pa_StartStream(_stream);<br>+ if (_err) {<br>+ LOG_ERROR("PortAudio: write errcode: %d message: %s", _err, Pa_GetErrorText(_err));<br>+ }<br>+ _stopped = 0;<br>
+ } <br>+ _err = Pa_WriteStream(_stream, frame, WavePlaybackAbstract::FRAME_SIZE);<br>+ if (_err == paStreamIsStopped) {<br>+ _err = Pa_StartStream(_stream);<br>+ _stopped = 0;<br>+ }<br>+ // FIXME: error handling?<br>
+ if (_err) {<br>+// Pa_AbortStream(_stream);<br>+// Pa_CloseStream(_stream);<br>+ LOG_ERROR("PortAudio dang: num %d message: %s", _err, Pa_GetErrorText(_err));<br>+ } else {<br>+ //Pa_CloseStream(_stream);<br>
+ }<br>+#endif<br> return true;<br> }<br> <br> void WavePlayer::stop()<br> {<br>+#if defined(__LINUX__)<br> snd_pcm_drain(_pcm);<br> snd_pcm_prepare(_pcm);<br>+#elif defined(__MACH__) || defined(__APPLE__)<br>
+ _err = Pa_StopStream(_stream);<br>+ if (_err != paNoError) {<br>+ DBG(0, "PortAudio stop: errcode %d message: %s", _err, Pa_GetErrorText(_err));<br>+ }<br>+ _stopped = 1;<br>+#endif<br> }<br> <br>
bool WavePlayer::abort()<br>@@ -206,6 +271,7 @@ bool WavePlayer::abort()<br> <br> uint32_t WavePlayer::get_delay_ms()<br> {<br>+#if defined(__LINUX__)<br> ASSERT(_pcm);<br> <br> snd_pcm_sframes_t delay;<br>@@ -214,5 +280,9 @@ uint32_t WavePlayer::get_delay_ms()<br>
return 0;<br> }<br> return delay / _sampels_per_ms;<br>+#else<br>+ // anything more precise than this?<br>+ return WavePlaybackAbstract::FRAME_SIZE / _sampels_per_ms;<br>+#endif<br> }<br> <br>diff --git a/client/x11/playback.h b/client/x11/playback.h<br>
index d8efd7e..f503ed5 100644<br>--- a/client/x11/playback.h<br>+++ b/client/x11/playback.h<br>@@ -18,7 +18,11 @@<br> #ifndef _H_LINUX_PLAYBACK<br> #define _H_LINUX_PLAYBACK<br> <br>+#if defined(__LINUX__)<br> #include <alsa/asoundlib.h><br>
+#elif defined(__MACH__) || defined(__APPLE__)<br>+#include <portaudio.h><br>+#endif<br> <br> #include "common.h"<br> #include "audio_devices.h"<br>@@ -38,9 +42,16 @@ private:<br> void cleanup();<br>
<br> private:<br>+#if defined(__LINUX__)<br> snd_pcm_t* _pcm;<br> snd_pcm_hw_params_t* _hw_params;<br> snd_pcm_sw_params_t* _sw_params;<br>+#elif defined(__MACH__) || defined(__APPLE__)<br>+ PaStreamParameters _outputParameters;<br>
+ PaStream *_stream;<br>+ PaError _err;<br>+ unsigned char _stopped; <br>+#endif<br> uint32_t _sampels_per_ms;<br> };<br> <br>diff --git a/client/x11/record.cpp b/client/x11/record.cpp<br>index 4ef9228..77cba4a 100644<br>
--- a/client/x11/record.cpp<br>+++ b/client/x11/record.cpp<br>@@ -47,9 +47,6 @@ WaveRecorder::WaveRecorder(Platform::RecordClient& client,<br> uint32_t bits_per_sample,<br> uint32_t channels)<br>
: _client (client)<br>- , _pcm (NULL)<br>- , _hw_params (NULL)<br>- , _sw_params (NULL)<br> , _sample_bytes (bits_per_sample * channels / 8)<br> , _frame (new uint8_t[_sample_bytes * WaveRecordAbstract::FRAME_SIZE])<br>
, _frame_pos (_frame)<br>@@ -74,7 +71,7 @@ void WaveRecorder::cleanup()<br> _client.remove_event_source(*_event_trigger);<br> delete _event_trigger;<br> }<br>-<br>+#if defined(__LINUX__)<br> if (_sw_params) {<br>
snd_pcm_sw_params_free(_sw_params);<br> }<br>@@ -86,12 +83,14 @@ void WaveRecorder::cleanup()<br> if (_pcm) {<br> snd_pcm_close(_pcm);<br> }<br>+#endif<br> }<br> <br> bool WaveRecorder::init(uint32_t sampels_per_sec,<br>
uint32_t bits_per_sample,<br> uint32_t channels)<br> {<br>+#if defined(__LINUX__)<br> const int frame_size = WaveRecordAbstract::FRAME_SIZE;<br> const char* pcm_device = "hw:0,0"; // "default" ???<br>
snd_pcm_format_t format;<br>@@ -195,21 +194,26 @@ bool WaveRecorder::init(uint32_t sampels_per_sec,<br> }<br> _event_trigger = new WaveRecorder::EventTrigger(*this, pfd.fd);<br> _client.add_event_source(*_event_trigger);<br>
+#endif<br> return true;<br> }<br> <br> void WaveRecorder::start()<br> {<br> _frame_pos = _frame;<br>+#if defined(__LINUX__)<br> snd_pcm_prepare(_pcm);<br> snd_pcm_start(_pcm);<br> snd_pcm_nonblock(_pcm, 1);<br>
+#endif<br> }<br> <br> void WaveRecorder::stop()<br> {<br>+#if defined(__LINUX__)<br> snd_pcm_drop(_pcm);<br> snd_pcm_prepare(_pcm);<br>+#endif<br> }<br> <br> bool WaveRecorder::abort()<br>@@ -219,6 +223,7 @@ bool WaveRecorder::abort()<br>
<br> void WaveRecorder::on_event()<br> {<br>+#if defined(__LINUX__)<br> for (;;) {<br> snd_pcm_sframes_t size = (_frame_end - _frame_pos) / _sample_bytes;<br> size = snd_pcm_readi(_pcm, _frame_pos, size);<br>
@@ -234,5 +239,6 @@ void WaveRecorder::on_event()<br> _frame_pos = _frame;<br> }<br> }<br>+#endif<br> }<br> <br>diff --git a/client/x11/record.h b/client/x11/record.h<br>index fde58c7..0181b09 100644<br>
--- a/client/x11/record.h<br>+++ b/client/x11/record.h<br>@@ -18,7 +18,9 @@<br> #ifndef _H_LINUX_RECORD<br> #define _H_LINUX_RECORD<br> <br>+#if defined(__LINUX__)<br> #include <alsa/asoundlib.h><br>+#endif<br> <br>
#include "common.h"<br> #include "audio_devices.h"<br>@@ -45,9 +47,11 @@ private:<br> <br> private:<br> Platform::RecordClient& _client;<br>+#if defined(__LINUX__) <br> snd_pcm_t* _pcm;<br>
snd_pcm_hw_params_t* _hw_params;<br> snd_pcm_sw_params_t* _sw_params;<br>+#endif<br> uint32_t _sample_bytes;<br> uint8_t* _frame;<br> uint8_t* _frame_pos;<br>diff --git a/client/x11/red_window.cpp b/client/x11/red_window.cpp<br>
index 5a0886a..c2a0975 100644<br>--- a/client/x11/red_window.cpp<br>+++ b/client/x11/red_window.cpp<br>@@ -55,7 +55,6 @@<br> <br> static Display* x_display = NULL;<br> static XContext user_data_context;<br>-static bool using_evdev = false;<br>
static XIC x_input_context = NULL;<br> <br> static Atom wm_protocol_atom;<br>@@ -73,7 +72,10 @@ static Atom wm_user_time;<br> static RedWindow* focus_window;<br> static unsigned long focus_serial = 0;<br> <br>+#ifndef __MACH__ || __APPLE__<br>
#define USE_X11_KEYCODE<br>+static bool using_evdev = false;<br>+#endif<br> <br> #ifdef USE_X11_KEYCODE<br> <br>diff --git a/common/lines.c b/common/lines.c<br>index d2e997e..1c67692 100644<br>--- a/common/lines.c<br>+++ b/common/lines.c<br>
@@ -2986,6 +2986,7 @@ miWideDashSegment (GCPtr pGC,<br> double k;<br> PolyVertexRec vertices[4];<br> PolyVertexRec saveRight, saveBottom;<br>+ saveRight.x = saveRight.y = saveBottom.x = saveBottom.y = 0;<br>
PolySlopeRec slopes[4];<br> PolyEdgeRec left[2], right[2];<br> LineFaceRec lcapFace, rcapFace;<br>diff --git a/<a href="http://configure.ac">configure.ac</a> b/<a href="http://configure.ac">configure.ac</a><br>
index 3369dde..6ef5938 100644<br>--- a/<a href="http://configure.ac">configure.ac</a><br>+++ b/<a href="http://configure.ac">configure.ac</a><br>@@ -59,11 +59,16 @@ case "$host" in<br> esac<br> AC_MSG_RESULT([$os_win32])<br>
<br>+AC_MSG_CHECKING([for Mac OS X])<br> case $host in<br> *-*-linux*)<br> os_linux=yes<br> ;;<br>+ *darwin*|*macosx*)<br>+ os_mac=yes<br>+ ;;<br> esac<br>+AC_MSG_RESULT([$os_mac])<br> <br> dnl =========================================================================<br>
dnl Check OS target<br>@@ -88,7 +93,7 @@ AC_SUBST(red_target)<br> AM_CONDITIONAL(OS_WIN32, test "$os_win32" = "yes")<br> AM_CONDITIONAL(OS_UNIX, test "$os_win32" != "yes")<br> AM_CONDITIONAL(OS_LINUX, test "$os_linux" = "yes")<br>
-<br>+AM_CONDITIONAL(OS_MAC, test "$os_mac" = "yes")<br> dnl =========================================================================<br> dnl Chek optional features<br> have_tunnel=no<br>@@ -119,12 +124,15 @@ AC_SUBST(PROTOCOL_CFLAGS)<br>
<br> AC_CHECK_LIBM<br> AC_SUBST(LIBM)<br>-<br>-AC_CHECK_LIB(rt, clock_gettime,<br>+if test "x$os_mac" != "yes"; then<br>+ AC_CHECK_LIB(rt, clock_gettime,<br> AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [Defined if we have clock_gettime()])<br>
LIBRT=-lrt<br> )<br>-AC_SUBST(LIBRT)<br>+ AC_SUBST(LIBRT)<br>+else<br>+ LIBRT=""<br>+fi<br> <br> SPICE_NONPKGCONFIG_LIBS+=" -pthread $LIBM $LIBRT"<br> <br>@@ -157,10 +165,17 @@ AC_SUBST(CELT051_LIBS)<br>
AC_SUBST(CELT051_LIBDIR)<br> SPICE_REQUIRES+=" celt051 >= 0.5.1.1"<br> <br>-PKG_CHECK_MODULES(ALSA, alsa)<br>-AC_SUBST(ALSA_CFLAGS)<br>-AC_SUBST(ALSA_LIBS)<br>-SPICE_REQUIRES+=" alsa"<br>+if test "x$os_mac" != "xyes"; then<br>
+ PKG_CHECK_MODULES(ALSA, alsa)<br>+ AC_SUBST(ALSA_CFLAGS)<br>+ AC_SUBST(ALSA_LIBS)<br>+ SPICE_REQUIRES+=" alsa"<br>+else<br>+ PKG_CHECK_MODULES(PA, portaudio-2.0 >= 2.0)<br>+ AC_SUBST(PA_CFLAGS)<br>
+ AC_SUBST(PA_LIBS)<br>+ SPICE_REQUIRES+=" portaudio-2.0"<br>+fi<br> <br> PKG_CHECK_MODULES(SSL, openssl)<br> AC_SUBST(SSL_CFLAGS)<br><br><br><br><a href="http://www.zsuatt.com/" target="_blank"></a>