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&#39;t complain<br>- fixed socket flags (MSG_NOSIGNAL =&gt; 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&#39;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 &amp;&amp; mon) {<br>
             size = mon-&gt;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 &lt;spice/error_codes.h&gt;<br> #include &quot;utils.h&quot;<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&amp; dst_addr, uint16_t dst_port,<br>                                  ProcessLoop&amp; process_loop, EventHandler&amp; 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&amp; in_name, char in_short_name,<br>
                               OptionType in_type, const std::string&amp; 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 &lt;&lt; basename(_argv[0]) &lt;&lt; &quot; - &quot; &lt;&lt; _description.c_str() &lt;&lt; &quot;\n\noptions:\n\n&quot;;<br>+    os &lt;&lt; _argv[0] &lt;&lt; &quot; - &quot; &lt;&lt; _description.c_str() &lt;&lt; &quot;\n\noptions:\n\n&quot;;<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 &quot;debug.h&quot;<br> #ifdef WIN32<br> #include &lt;sys/timeb.h&gt;<br>+#elif defined(__MACH__) || defined(__APPLE__)<br>+#include &lt;sys/time.h&gt;<br>+#define _x_min(a, b) ((a) &lt; (b) ? (a) : (b))<br>
+#if _POSIX_TIMERS &lt;= 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 &gt; 0 ? 10000000 : _x_min(remaining.tv_nsec,10000000));<br>
+        nanosleep(&amp;ts, &amp;slept);<br>+        ts.tv_nsec -= slept.tv_nsec;<br>+        if (ts.tv_nsec &lt;= 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 &lt; 0 || (!remaining.tv_sec &amp;&amp; remaining.tv_nsec &lt;= 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&amp; time, uint64_t delta_nano)<br>
     _ftime_s(&amp;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(&amp;tv,NULL);<br>
+    time.tv_sec = tv.tv_sec;<br>+    time.tv_nsec = tv.tv_usec*1000;<br> #else<br>     clock_gettime(CLOCK_MONOTONIC, &amp;time);<br> #endif<br>@@ -75,10 +112,13 @@ Condition::Condition()<br> #else<br>     pthread_condattr_t attr;<br>
     pthread_condattr_init(&amp;attr);<br>+#if defined(__MACH__) || defined(__APPLE__)<br>+#else<br>     int r;<br>     if ((r = pthread_condattr_setclock(&amp;attr, CLOCK_MONOTONIC))) {<br>         THROW(&quot;set clock failed %d&quot;, r);<br>
     }<br>+#endif<br>     pthread_cond_init(&amp;_condition, &amp;attr);<br>     pthread_condattr_destroy(&amp;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 &lt;<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>&gt;.<br>+   You should have received a copy of the GNU General Public License<br>
+   along with this program.  If not, see &lt;<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>&gt;.<br> */<br> <br> #ifndef _H_ATOMIC_COUNT<br> #define _H_ATOMIC_COUNT<br> <br>+#if defined(__MACH__) || defined(__APPLE__)<br>
+#include &lt;stdint.h&gt;<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 &lt;<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>&gt;.<br>
+   You should have received a copy of the GNU General Public License<br>+   along with this program.  If not, see &lt;<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>&gt;.<br> */<br>-<br>-#include &lt;sys/epoll.h&gt;<br>
 #include &lt;sys/fcntl.h&gt;<br>-<br> #include &quot;event_sources.h&quot;<br>-#include &quot;debug.h&quot;<br>-#include &quot;utils.h&quot;<br>-<br>-#ifdef USING_EVENT_FD<br>-#include &lt;sys/eventfd.h&gt;<br>-#endif<br>
 <br>-#define NUM_EPOLL_EVENTS 10<br>+#include &lt;sys/select.h&gt;<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 &quot;debug.h&quot;<br>+#include &quot;utils.h&quot;<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(&quot;create epool failed&quot;);<br>+    FD_ZERO(&amp;_readfds);<br>+    FD_ZERO(&amp;_writefds);<br>+    for(int i=0;i&lt;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, &amp;tmpreadfds, &amp;tmpwritefds, NULL, NULL);<br>+    } else {<br>+    num_events = select(_hfd+1, &amp;tmpreadfds, &amp;tmpwritefds, NULL, &amp;tmout);<br>
+    }<br>     if (num_events == -1) {<br>-        if (errno == EINTR) {<br>-            return false;<br>-        }<br>-        THROW(&quot;wait error eventfd failed&quot;);<br>+    //THROW(&quot;select() failed&quot;);<br>
+        return false;<br>+    } else if (num_events == 0) {<br>+    return false;<br>+    } else {<br>+    for (int i = 0; i &lt; NUM_EVENTS; i++) {<br>+        if ((_lofds[i].rw &amp;&amp; FD_ISSET(_lofds[i].fd, &amp;tmpwritefds)) ||<br>
+            FD_ISSET(_lofds[i].fd, &amp;tmpreadfds)) {<br>+            EventWrapper* wrapper;<br>+            EventSource* event;<br>+            wrapper = (EventWrapper *)_lofds[i].wrapper;<br>+            wrapper-&gt;ref();<br>
+            if ((event = wrapper-&gt;get_event())) {<br>+                event-&gt;action();<br>+            }<br>+            wrapper-&gt;unref();<br>+        }<br>+    }<br>     }<br>+    return false;<br>+}<br> <br>-    for (int i = 0; i &lt; num_events; i++) {<br>
-        ((EventWrapper*)events[i].data.ptr)-&gt;ref();<br>+void EventSources_p::add_fd(int fd,EventWrapper *wrapper, unsigned char rw) { <br>+    // save the highest fd<br>+    if (fd &gt; _hfd)<br>+    _hfd = fd;<br>+    for(int i=0;i&lt;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, &amp;_readfds);<br>+        if (rw)<br>+            FD_SET(fd, &amp;_writefds);<br>
+        break;<br>+    }<br>     }<br>+}<br> <br>-    for (int i = 0; i &lt; num_events; i++) {<br>-        EventWrapper* wrapper;<br>-        EventSource* event;<br>-<br>-        wrapper = (EventWrapper *)events[i].data.ptr;<br>
-        if ((event = wrapper-&gt;get_event())) {<br>-            event-&gt;action();<br>-        }<br>-        wrapper-&gt;unref();<br>-    }<br>-    return false;<br>+void EventSources_p::del_fd(int fd) {<br>+   for(int i=0;i&lt;NUM_EVENTS;i++) {<br>
+    if (_lofds[i].fd == fd) {<br>+        FD_CLR(fd, &amp;_readfds);<br>+        if (_lofds[i].rw)<br>+            FD_CLR(fd, &amp;_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&amp; 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, &amp;event) == -1) {<br>-        THROW(&quot;epoll add failed&quot;);<br>-    }<br>
+    add_fd(fd, wrapper, 0);<br>     _events.push_back(wrapper);<br> }<br> <br>@@ -162,26 +188,18 @@ void EventSources::remove_trigger(Trigger&amp; trigger)<br>         }<br>     }<br>     int fd = trigger.get_fd();<br>-    if (epoll_ctl(_epoll, EPOLL_CTL_DEL, fd, NULL) == -1) {<br>
-        THROW(&quot;epoll remove failed&quot;);<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(&quot;create eventfd failed&quot;);<br>-    }<br>-#else<br>     int fd[2];<br>     if (pipe(fd) == -1) {<br>         THROW(&quot;create pipe failed&quot;);<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(&quot;failed to set eventfd non block: %s&quot;, 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, &amp;event) == -1) {<br>
-        THROW(&quot;epoll add failed&quot;);<br>-    }<br>-}<br>-<br>-void EventSources::add_socket(Socket&amp; 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&amp; events, EventSource&amp; event)<br> {<br>     EventSources_p::Events::iterator iter = events.begin();<br>
@@ -296,6 +292,15 @@ static bool remove_event(EventSources_p::Events&amp; events, EventSource&amp; event)<br>         }<br>     }<br> }<br>+void EventSources::add_socket(Socket&amp; 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&amp; socket)<br> {<br>@@ -303,9 +308,7 @@ void EventSources::remove_socket(Socket&amp; socket)<br>         THROW(&quot;socket not found&quot;);<br>     }<br>     int fd = socket.get_socket();<br>
-    if (epoll_ctl(_epoll, EPOLL_CTL_DEL, fd, NULL) == -1) {<br>-        THROW(&quot;epoll remove failed&quot;);<br>-    }<br>+    del_fd(fd);<br>     set_blocking(fd);<br> }<br> <br>@@ -314,7 +317,7 @@ void EventSources::add_file(File&amp; 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&amp; file)<br>         THROW(&quot;file not found&quot;);<br>     }<br>     int fd = file.get_fd();<br>-    if (epoll_ctl(_epoll, EPOLL_CTL_DEL, fd, NULL) == -1) {<br>
-        THROW(&quot;epoll remove failed&quot;);<br>-    }<br>+    del_fd(fd);<br>     set_blocking(fd);<br> }<br> <br>@@ -337,3 +338,4 @@ void EventSources::add_handle(Handle&amp; file)<br> void EventSources::remove_handle(Handle&amp; 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 &lt;<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>&gt;.<br>+   You should have received a copy of the GNU General Public License<br>
+   along with this program.  If not, see &lt;<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>&gt;.<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 &lt;sys/select.h&gt;<br> <br> #include &quot;common.h&quot;<br> #include &quot;threads.h&quot;<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&lt;EventWrapper*&gt; 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 &lt;sys/types.h&gt;<br> #include &lt;sys/socket.h&gt;<br>-#include &lt;sys/epoll.h&gt;<br>
+//#include &lt;sys/epoll.h&gt;<br> #include &lt;sys/un.h&gt;<br> #include &quot;named_pipe.h&quot;<br> #include &quot;utils.h&quot;<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 &lt;X11/extensions/XShm.h&gt;<br> #include &lt;unistd.h&gt;<br> #include &lt;sys/socket.h&gt;<br>-#include &lt;sys/epoll.h&gt;<br>
+//#include &lt;sys/epoll.h&gt;<br> #include &lt;sys/resource.h&gt;<br> #include &lt;sys/types.h&gt;<br> #include &lt;sys/syscall.h&gt;<br>-#include &lt;sys/stat.h&gt;<br> #include &lt;unistd.h&gt;<br> #include &lt;fcntl.h&gt;<br>
 #include &lt;set&gt;<br>-#include &lt;values.h&gt;<br> #include &lt;signal.h&gt;<br> #include &lt;config.h&gt;<br> #include &lt;sys/shm.h&gt;<br>@@ -61,6 +59,18 @@<br> #define BOOL bool<br> #include &quot;named_pipe.h&quot;<br>
 <br>+#if defined(__LINUX__)<br>+#include &lt;values.h&gt;<br>+#elif defined(__APPLE__) || defined(__MACH__)<br>+#include &lt;float.h&gt;<br>+#include &lt;machine/limits.h&gt;<br>+#include &lt;sys/time.h&gt;<br>+#include &lt;sys/stat.h&gt;<br>
+#include &lt;assert.h&gt;<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, &amp;time_space);<br>+#elif defined(__APPLE__) || defined(__MACH__)<br>
+    struct timeval tv;<br>+    gettimeofday(&amp;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__) &amp;&amp; !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 = &quot;default&quot;;<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(&amp;_pcm, pcm_device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) &lt; 0) {<br>         LOG_ERROR(&quot;cannot open audio playback device %s %s&quot;, pcm_device, snd_strerror(err));<br>
@@ -174,12 +177,44 @@ bool WavePlayer::init(uint32_t sampels_per_sec,<br>         LOG_ERROR(&quot;cannot prepare pcm device %s&quot;, 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(&quot;PortAudio initialization failure!&quot;);<br>+    return false;<br>+    }<br>+    <br>+    _outputParameters.device = Pa_GetDefaultOutputDevice();<br>
+    if (_outputParameters.device == paNoDevice) {<br>+    LOG_ERROR(&quot;PortAudio: No dfault output device&quot;);<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)-&gt;defaultLowOutputLatency;<br>+    _err = Pa_OpenStream(&amp;_stream, NULL, &amp;_outputParameters, sampels_per_sec, frame_size, paClipOff, NULL, NULL);<br>
+    if (_err != paNoError) {<br>+    DBG(0,&quot;PortAudio: Error number: %d message: %s&quot;, _err, Pa_GetErrorText(_err));<br>+    return false;<br>+    } <br>+    DBG(0, &quot;Initialized PortAudio: samples/sec: %d channels: %d bits/sample: %d&quot;,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 &lt; 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(&quot;PortAudio: write errcode: %d message: %s&quot;, _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(&quot;PortAudio dang: num %d message: %s&quot;, _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, &quot;PortAudio stop: errcode %d message: %s&quot;, _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 &lt;alsa/asoundlib.h&gt;<br>
+#elif defined(__MACH__) || defined(__APPLE__)<br>+#include &lt;portaudio.h&gt;<br>+#endif<br> <br> #include &quot;common.h&quot;<br> #include &quot;audio_devices.h&quot;<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&amp; 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 = &quot;hw:0,0&quot;; // &quot;default&quot; ???<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 &lt;alsa/asoundlib.h&gt;<br>+#endif<br> <br>
 #include &quot;common.h&quot;<br> #include &quot;audio_devices.h&quot;<br>@@ -45,9 +47,11 @@ private:<br> <br> private:<br>     Platform::RecordClient&amp; _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 &quot;$host&quot; 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 &quot;$os_win32&quot; = &quot;yes&quot;)<br> AM_CONDITIONAL(OS_UNIX, test &quot;$os_win32&quot; != &quot;yes&quot;)<br> AM_CONDITIONAL(OS_LINUX, test &quot;$os_linux&quot; = &quot;yes&quot;)<br>
-<br>+AM_CONDITIONAL(OS_MAC, test &quot;$os_mac&quot; = &quot;yes&quot;)<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 &quot;x$os_mac&quot; != &quot;yes&quot;; 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=&quot;&quot;<br>+fi<br> <br> SPICE_NONPKGCONFIG_LIBS+=&quot; -pthread $LIBM $LIBRT&quot;<br> <br>@@ -157,10 +165,17 @@ AC_SUBST(CELT051_LIBS)<br>
 AC_SUBST(CELT051_LIBDIR)<br> SPICE_REQUIRES+=&quot; celt051 &gt;= 0.5.1.1&quot;<br> <br>-PKG_CHECK_MODULES(ALSA, alsa)<br>-AC_SUBST(ALSA_CFLAGS)<br>-AC_SUBST(ALSA_LIBS)<br>-SPICE_REQUIRES+=&quot; alsa&quot;<br>+if test &quot;x$os_mac&quot; != &quot;xyes&quot;; then<br>
+     PKG_CHECK_MODULES(ALSA, alsa)<br>+     AC_SUBST(ALSA_CFLAGS)<br>+     AC_SUBST(ALSA_LIBS)<br>+     SPICE_REQUIRES+=&quot; alsa&quot;<br>+else<br>+     PKG_CHECK_MODULES(PA, portaudio-2.0 &gt;= 2.0)<br>+     AC_SUBST(PA_CFLAGS)<br>
+     AC_SUBST(PA_LIBS)<br>+     SPICE_REQUIRES+=&quot; portaudio-2.0&quot;<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>