aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--3rdParty/EDID/edid.cpp446
-rw-r--r--3rdParty/EDID/edid.h174
-rw-r--r--ConfigurePermissionsAndPackages.sh51
-rw-r--r--LibOVR/90-oculus.rules2
-rw-r--r--LibOVR/Makefile278
-rw-r--r--LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp787
-rw-r--r--LibOVR/Src/OVR_Linux_DeviceManager.cpp331
-rw-r--r--LibOVR/Src/OVR_Linux_DeviceManager.h122
-rw-r--r--LibOVR/Src/OVR_Linux_HIDDevice.cpp819
-rw-r--r--LibOVR/Src/OVR_Linux_HIDDevice.h135
-rw-r--r--LibOVR/Src/OVR_Linux_HMDDevice.cpp291
-rw-r--r--LibOVR/Src/OVR_Linux_HMDDevice.h154
-rw-r--r--LibOVR/Src/OVR_Linux_SensorDevice.cpp57
-rw-r--r--Makefile82
-rw-r--r--OculusConfigurationUtility.sh28
-rw-r--r--OculusWorldDemo.sh25
-rw-r--r--Samples/CommonSrc/Platform/Linux_Gamepad.cpp453
-rw-r--r--Samples/CommonSrc/Platform/Linux_Gamepad.h83
-rw-r--r--Samples/CommonSrc/Platform/Linux_Platform.cpp784
-rw-r--r--Samples/CommonSrc/Platform/Linux_Platform.h147
-rw-r--r--Samples/OculusWorldDemo/Makefile153
21 files changed, 5402 insertions, 0 deletions
diff --git a/3rdParty/EDID/edid.cpp b/3rdParty/EDID/edid.cpp
new file mode 100644
index 0000000..c74def2
--- /dev/null
+++ b/3rdParty/EDID/edid.cpp
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2007 Red Hat, Inc.
+ *
+ * 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
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, 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 (including the next
+ * paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS 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.
+ */
+
+/* Author: Soren Sandmann <[email protected]> */
+#include "edid.h"
+#include <stdint.h>
+#include <math.h>
+#include <memory.h>
+#include <X11/Xatom.h>
+
+
+static int get_bit(int in, int bit) {
+ return (in & (1 << bit)) >> bit;
+}
+
+static int get_bits(int in, int begin, int end) {
+ int mask = (1 << (end - begin + 1)) - 1;
+
+ return (in >> begin) & mask;
+}
+
+static bool decode_header(const uint8_t *edid) {
+ if (memcmp(edid, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) == 0)
+ return true;
+ return false;
+}
+
+static int decode_vendor_and_product_identification(const uint8_t *edid, MonitorInfo *info) {
+
+ /* Manufacturer Code */
+ info->manufacturer_code[0] = get_bits(edid[0x08], 2, 6);
+ info->manufacturer_code[1] = get_bits(edid[0x08], 0, 1) << 3;
+ info->manufacturer_code[1] |= get_bits(edid[0x09], 5, 7);
+ info->manufacturer_code[2] = get_bits(edid[0x09], 0, 4);
+ info->manufacturer_code[3] = '\0';
+
+ info->manufacturer_code[0] += 'A' - 1;
+ info->manufacturer_code[1] += 'A' - 1;
+ info->manufacturer_code[2] += 'A' - 1;
+
+ /* Product Code */
+ info->product_code = edid[0x0b] << 8 | edid[0x0a];
+
+ /* Serial Number */
+ info->serial_number = edid[0x0c] | edid[0x0d] << 8 | edid[0x0e] << 16 | edid[0x0f] << 24;
+
+ /* Week and Year */
+ bool is_model_year = false;
+ switch (edid[0x10]) {
+ case 0x00:
+ info->production_week = -1;
+ break;
+
+ case 0xff:
+ info->production_week = -1;
+ is_model_year = true;
+ break;
+
+ default:
+ info->production_week = edid[0x10];
+ break;
+ }
+
+ if (is_model_year) {
+ info->production_year = -1;
+ info->model_year = 1990 + edid[0x11];
+ } else {
+ info->production_year = 1990 + edid[0x11];
+ info->model_year = -1;
+ }
+
+ return true;
+}
+
+static bool decode_edid_version(const uint8_t *edid, MonitorInfo *info) {
+ info->major_version = edid[0x12];
+ info->minor_version = edid[0x13];
+ return true;
+}
+
+static bool decode_display_parameters(const uint8_t *edid, MonitorInfo *info) {
+ /* Digital vs Analog */
+ info->is_digital = get_bit(edid[0x14], 7);
+
+ if (info->is_digital) {
+ static const int bit_depth[8] = { -1, 6, 8, 10, 12, 14, 16, -1 };
+ static const Interface interfaces[6] = { UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT };
+
+ int bits = get_bits(edid[0x14], 4, 6);
+ info->connector.digital.bits_per_primary = bit_depth[bits];
+
+ bits = get_bits(edid[0x14], 0, 3);
+ if (bits <= 5)
+ info->connector.digital.interface = interfaces[bits];
+ else
+ info->connector.digital.interface = UNDEFINED;
+ } else {
+ int bits = get_bits(edid[0x14], 5, 6);
+ static const double levels[][3] = { //
+ { 0.7, 0.3, 1.0 }, //
+ { 0.714, 0.286, 1.0 }, //
+ { 1.0, 0.4, 1.4 }, //
+ { 0.7, 0.0, 0.7 }, //
+ };
+
+ info->connector.analog.video_signal_level = levels[bits][0];
+ info->connector.analog.sync_signal_level = levels[bits][1];
+ info->connector.analog.total_signal_level = levels[bits][2];
+ info->connector.analog.blank_to_black = get_bit(edid[0x14], 4);
+ info->connector.analog.separate_hv_sync = get_bit(edid[0x14], 3);
+ info->connector.analog.composite_sync_on_h = get_bit(edid[0x14], 2);
+ info->connector.analog.composite_sync_on_green = get_bit(edid[0x14], 1);
+ info->connector.analog.serration_on_vsync = get_bit(edid[0x14], 0);
+ }
+
+ /* Screen Size / Aspect Ratio */
+ if (edid[0x15] == 0 && edid[0x16] == 0) {
+ info->width_mm = -1;
+ info->height_mm = -1;
+ info->aspect_ratio = -1.0;
+ } else if (edid[0x16] == 0) {
+ info->width_mm = -1;
+ info->height_mm = -1;
+ info->aspect_ratio = 100.0 / (edid[0x15] + 99);
+ } else if (edid[0x15] == 0) {
+ info->width_mm = -1;
+ info->height_mm = -1;
+ info->aspect_ratio = 100.0 / (edid[0x16] + 99);
+ info->aspect_ratio = 1 / info->aspect_ratio; /* portrait */
+ } else {
+ info->width_mm = 10 * edid[0x15];
+ info->height_mm = 10 * edid[0x16];
+ }
+
+ /* Gamma */
+ if (edid[0x17] == 0xFF)
+ info->gamma = -1.0;
+ else
+ info->gamma = (edid[0x17] + 100.0) / 100.0;
+
+ /* Features */
+ info->standby = get_bit(edid[0x18], 7);
+ info->suspend = get_bit(edid[0x18], 6);
+ info->active_off = get_bit(edid[0x18], 5);
+
+ if (info->is_digital) {
+ info->connector.digital.rgb444 = 1;
+ if (get_bit(edid[0x18], 3))
+ info->connector.digital.ycrcb444 = 1;
+ if (get_bit(edid[0x18], 4))
+ info->connector.digital.ycrcb422 = 1;
+ } else {
+ int bits = get_bits(edid[0x18], 3, 4);
+ ColorType color_type[4] = { MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR };
+
+ info->connector.analog.color_type = color_type[bits];
+ }
+
+ info->srgb_is_standard = get_bit(edid[0x18], 2);
+
+ /* In 1.3 this is called "has preferred timing" */
+ info->preferred_timing_includes_native = get_bit(edid[0x18], 1);
+
+ /* FIXME: In 1.3 this indicates whether the monitor accepts GTF */
+ info->continuous_frequency = get_bit(edid[0x18], 0);
+ return true;
+}
+
+static double decode_fraction(int high, int low) {
+ double result = 0.0;
+ high = (high << 2) | low;
+ for (int i = 0; i < 10; ++i)
+ result += get_bit(high, i) * pow(2, i - 10);
+ return result;
+}
+
+static bool decode_color_characteristics(const uint8_t *edid, MonitorInfo *info) {
+ info->red_x = decode_fraction(edid[0x1b], get_bits(edid[0x19], 6, 7));
+ info->red_y = decode_fraction(edid[0x1c], get_bits(edid[0x19], 5, 4));
+ info->green_x = decode_fraction(edid[0x1d], get_bits(edid[0x19], 2, 3));
+ info->green_y = decode_fraction(edid[0x1e], get_bits(edid[0x19], 0, 1));
+ info->blue_x = decode_fraction(edid[0x1f], get_bits(edid[0x1a], 6, 7));
+ info->blue_y = decode_fraction(edid[0x20], get_bits(edid[0x1a], 4, 5));
+ info->white_x = decode_fraction(edid[0x21], get_bits(edid[0x1a], 2, 3));
+ info->white_y = decode_fraction(edid[0x22], get_bits(edid[0x1a], 0, 1));
+
+ return true;
+}
+
+static bool decode_established_timings(const uint8_t *edid, MonitorInfo *info) {
+ static const Timing established[][8] = { //
+ { { 800, 600, 60 }, { 800, 600, 56 }, //
+ { 640, 480, 75 }, { 640, 480, 72 }, //
+ { 640, 480, 67 }, { 640, 480, 60 }, //
+ { 720, 400, 88 }, { 720, 400, 70 } }, //
+ { { 1280, 1024, 75 }, { 1024, 768, 75 }, //
+ { 1024, 768, 70 }, { 1024, 768, 60 }, //
+ { 1024, 768, 87 }, { 832, 624, 75 }, //
+ { 800, 600, 75 }, { 800, 600, 72 } }, //
+ { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, //
+ { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1152, 870, 75 } }, //
+ };
+
+ int idx = 0;
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 8; ++j) {
+ int byte = edid[0x23 + i];
+
+ if (get_bit(byte, j) && established[i][j].frequency != 0)
+ info->established[idx++] = established[i][j];
+ }
+ }
+ return true;
+}
+
+static bool decode_standard_timings(const uint8_t *edid, MonitorInfo *info) {
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ int first = edid[0x26 + 2 * i];
+ int second = edid[0x27 + 2 * i];
+
+ if (first != 0x01 && second != 0x01) {
+ int w = 8 * (first + 31);
+ int h = 0;
+
+ switch (get_bits(second, 6, 7)) {
+ case 0x00:
+ h = (w / 16) * 10;
+ break;
+ case 0x01:
+ h = (w / 4) * 3;
+ break;
+ case 0x02:
+ h = (w / 5) * 4;
+ break;
+ case 0x03:
+ h = (w / 16) * 9;
+ break;
+ }
+
+ info->standard[i].width = w;
+ info->standard[i].height = h;
+ info->standard[i].frequency = get_bits(second, 0, 5) + 60;
+ }
+ }
+
+ return true;
+}
+
+static void decode_lf_string(const uint8_t *s, int n_chars, char *result) {
+ int i;
+ for (i = 0; i < n_chars; ++i) {
+ if (s[i] == 0x0a) {
+ *result++ = '\0';
+ break;
+ } else if (s[i] == 0x00) {
+ /* Convert embedded 0's to spaces */
+ *result++ = ' ';
+ } else {
+ *result++ = s[i];
+ }
+ }
+}
+
+static void decode_display_descriptor(const uint8_t *desc, MonitorInfo *info) {
+ switch (desc[0x03]) {
+ case 0xFC:
+ decode_lf_string(desc + 5, 13, info->dsc_product_name);
+ break;
+ case 0xFF:
+ decode_lf_string(desc + 5, 13, info->dsc_serial_number);
+ break;
+ case 0xFE:
+ decode_lf_string(desc + 5, 13, info->dsc_string);
+ break;
+ case 0xFD:
+ /* Range Limits */
+ break;
+ case 0xFB:
+ /* Color Point */
+ break;
+ case 0xFA:
+ /* Timing Identifications */
+ break;
+ case 0xF9:
+ /* Color Management */
+ break;
+ case 0xF8:
+ /* Timing Codes */
+ break;
+ case 0xF7:
+ /* Established Timings */
+ break;
+ case 0x10:
+ break;
+ }
+}
+
+static void decode_detailed_timing(const uint8_t *timing, DetailedTiming *detailed) {
+ int bits;
+ StereoType stereo[] = { //
+ NO_STEREO, NO_STEREO, //
+ FIELD_RIGHT, FIELD_LEFT, //
+ TWO_WAY_RIGHT_ON_EVEN, TWO_WAY_LEFT_ON_EVEN, //
+ FOUR_WAY_INTERLEAVED, //
+ SIDE_BY_SIDE //
+ };
+
+ detailed->pixel_clock = (timing[0x00] | timing[0x01] << 8) * 10000;
+ detailed->h_addr = timing[0x02] | ((timing[0x04] & 0xf0) << 4);
+ detailed->h_blank = timing[0x03] | ((timing[0x04] & 0x0f) << 8);
+ detailed->v_addr = timing[0x05] | ((timing[0x07] & 0xf0) << 4);
+ detailed->v_blank = timing[0x06] | ((timing[0x07] & 0x0f) << 8);
+ detailed->h_front_porch = timing[0x08] | get_bits(timing[0x0b], 6, 7) << 8;
+ detailed->h_sync = timing[0x09] | get_bits(timing[0x0b], 4, 5) << 8;
+ detailed->v_front_porch = get_bits(timing[0x0a], 4, 7) | get_bits(timing[0x0b], 2, 3) << 4;
+ detailed->v_sync = get_bits(timing[0x0a], 0, 3) | get_bits(timing[0x0b], 0, 1) << 4;
+ detailed->width_mm = timing[0x0c] | get_bits(timing[0x0e], 4, 7) << 8;
+ detailed->height_mm = timing[0x0d] | get_bits(timing[0x0e], 0, 3) << 8;
+ detailed->right_border = timing[0x0f];
+ detailed->top_border = timing[0x10];
+ detailed->interlaced = get_bit(timing[0x11], 7);
+
+ /* Stereo */
+ bits = get_bits(timing[0x11], 5, 6) << 1 | get_bit(timing[0x11], 0);
+ detailed->stereo = stereo[bits];
+
+ /* Sync */
+ bits = timing[0x11];
+
+ detailed->digital_sync = get_bit(bits, 4);
+ if (detailed->digital_sync) {
+ detailed->connector.digital.composite = !get_bit(bits, 3);
+ if (detailed->connector.digital.composite) {
+ detailed->connector.digital.serrations = get_bit(bits, 2);
+ detailed->connector.digital.negative_vsync = 0;
+ } else {
+ detailed->connector.digital.serrations = 0;
+ detailed->connector.digital.negative_vsync = !get_bit(bits, 2);
+ }
+ detailed->connector.digital.negative_hsync = !get_bit(bits, 0);
+ } else {
+ detailed->connector.analog.bipolar = get_bit(bits, 3);
+ detailed->connector.analog.serrations = get_bit(bits, 2);
+ detailed->connector.analog.sync_on_green = !get_bit(bits, 1);
+ }
+}
+
+static bool decode_descriptors(const uint8_t *edid, MonitorInfo *info) {
+ int timing_idx = 0;
+ for (int i = 0; i < 4; ++i) {
+ int index = 0x36 + i * 18;
+ if (edid[index + 0] == 0x00 && edid[index + 1] == 0x00) {
+ decode_display_descriptor(edid + index, info);
+ } else {
+ decode_detailed_timing(edid + index, &(info->detailed_timings[timing_idx++]));
+ }
+ }
+ info->n_detailed_timings = timing_idx;
+ return true;
+}
+
+static void decode_check_sum(const uint8_t *edid, MonitorInfo *info) {
+ uint8_t check = 0;
+ for (int i = 0; i < 128; ++i)
+ check += edid[i];
+ info->checksum = check;
+}
+
+MonitorInfo * decode_edid(const uint8_t *edid) {
+ MonitorInfo *info = new MonitorInfo();
+ decode_check_sum(edid, info);
+ if (decode_header(edid) && //
+ decode_vendor_and_product_identification(edid, info) && //
+ decode_edid_version(edid, info) && //
+ decode_display_parameters(edid, info) && //
+ decode_color_characteristics(edid, info) && //
+ decode_established_timings(edid, info) && //
+ decode_standard_timings(edid, info) && //
+ decode_descriptors(edid, info)) {
+ return info;
+ } else {
+ delete info;
+ return 0;
+ }
+}
+
+static uint8_t * get_property(Display *dpy, RROutput output, Atom atom, int *len) {
+ unsigned char *prop;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ Atom actual_type;
+ uint8_t *result = NULL;
+
+ XRRGetOutputProperty(dpy, output, atom, 0, 100, False, False,
+ AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop);
+
+ if (actual_type == XA_INTEGER && actual_format == 8) {
+ result = new uint8_t[nitems];
+ memcpy(result, prop, nitems);
+ if (len)
+ *len = nitems;
+ }
+ XFree(prop);
+ return result;
+}
+
+MonitorInfo * read_edid_data(Display * disp, RROutput id) {
+ int len;
+ Atom edid_atom = XInternAtom(disp, "EDID", false);
+ uint8_t *edid = get_property(disp, id, edid_atom, &len);
+ if (!edid) {
+ edid_atom = XInternAtom(disp, "EDID_DATA", false);
+ edid = get_property(disp, id, edid_atom, &len);
+ }
+
+ MonitorInfo * result = 0;
+ if (edid) {
+ if (len % 128 == 0) {
+ result = decode_edid(edid);
+ }
+ delete[] edid;
+ }
+
+ return result;
+}
diff --git a/3rdParty/EDID/edid.h b/3rdParty/EDID/edid.h
new file mode 100644
index 0000000..2d6cb20
--- /dev/null
+++ b/3rdParty/EDID/edid.h
@@ -0,0 +1,174 @@
+#include <X11/extensions/Xrandr.h>
+#include <X11/Xlib.h>
+
+/*
+ * Copyright 2007 Red Hat, Inc.
+ *
+ * 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
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, 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 (including the next
+ * paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS 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.
+ */
+
+/* Author: Soren Sandmann <[email protected]> */
+
+typedef enum {
+ UNDEFINED,
+ DVI,
+ HDMI_A,
+ HDMI_B,
+ MDDI,
+ DISPLAY_PORT
+} Interface;
+
+typedef enum {
+ UNDEFINED_COLOR,
+ MONOCHROME,
+ RGB,
+ OTHER_COLOR
+} ColorType;
+
+typedef enum {
+ NO_STEREO,
+ FIELD_RIGHT,
+ FIELD_LEFT,
+ TWO_WAY_RIGHT_ON_EVEN,
+ TWO_WAY_LEFT_ON_EVEN,
+ FOUR_WAY_INTERLEAVED,
+ SIDE_BY_SIDE
+} StereoType;
+
+struct Timing {
+ int width;
+ int height;
+ int frequency;
+};
+
+struct DetailedTiming {
+ int pixel_clock;
+ int h_addr;
+ int h_blank;
+ int h_sync;
+ int h_front_porch;
+ int v_addr;
+ int v_blank;
+ int v_sync;
+ int v_front_porch;
+ int width_mm;
+ int height_mm;
+ int right_border;
+ int top_border;
+ int interlaced;
+ StereoType stereo;
+
+ int digital_sync;
+ union {
+ struct {
+ int bipolar;
+ int serrations;
+ int sync_on_green;
+ } analog;
+
+ struct {
+ int composite;
+ int serrations;
+ int negative_vsync;
+ int negative_hsync;
+ } digital;
+ } connector;
+};
+
+struct MonitorInfo {
+ int checksum;
+ char manufacturer_code[4];
+ int product_code;
+ unsigned int serial_number;
+
+ int production_week; /* -1 if not specified */
+ int production_year; /* -1 if not specified */
+ int model_year; /* -1 if not specified */
+
+ int major_version;
+ int minor_version;
+
+ int is_digital;
+
+ union {
+ struct {
+ int bits_per_primary;
+ Interface interface;
+ int rgb444;
+ int ycrcb444;
+ int ycrcb422;
+ } digital;
+
+ struct {
+ double video_signal_level;
+ double sync_signal_level;
+ double total_signal_level;
+
+ int blank_to_black;
+
+ int separate_hv_sync;
+ int composite_sync_on_h;
+ int composite_sync_on_green;
+ int serration_on_vsync;
+ ColorType color_type;
+ } analog;
+ } connector;
+
+ int width_mm; /* -1 if not specified */
+ int height_mm; /* -1 if not specified */
+ double aspect_ratio; /* -1.0 if not specififed */
+
+ double gamma; /* -1.0 if not specified */
+
+ int standby;
+ int suspend;
+ int active_off;
+
+ int srgb_is_standard;
+ int preferred_timing_includes_native;
+ int continuous_frequency;
+
+ double red_x;
+ double red_y;
+ double green_x;
+ double green_y;
+ double blue_x;
+ double blue_y;
+ double white_x;
+ double white_y;
+
+ Timing established[24]; /* Terminated by 0x0x0 */
+ Timing standard[8];
+
+ int n_detailed_timings;
+ DetailedTiming detailed_timings[4]; /* If monitor has a preferred
+ * mode, it is the first one
+ * (whether it has, is
+ * determined by the
+ * preferred_timing_includes
+ * bit.
+ */
+
+ /* Optional product description */
+ char dsc_serial_number[14];
+ char dsc_product_name[14];
+ char dsc_string[14]; /* Unspecified ASCII data */
+};
+
+MonitorInfo * read_edid_data(Display * disp, RROutput id);
diff --git a/ConfigurePermissionsAndPackages.sh b/ConfigurePermissionsAndPackages.sh
new file mode 100644
index 0000000..44f550b
--- /dev/null
+++ b/ConfigurePermissionsAndPackages.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+#############################################################################
+#
+# Filename : ConfigurePermissionsAndPackages.sh
+# Content : Linux file for installing prerequisite libraries and the
+# permissions file for the USB HID device
+# Created : 2013
+# Authors : Simon Hallam and Brant Lewis
+# Copyright : Copyright 2013 OculusVR, Inc. All Rights Reserved
+# Instruction : Ensure that the install.sh has execute permissions.
+# Navigate to a command shell, enter:
+#
+# ./install.sh
+#
+# Enter the administrator password for sudo access.
+#
+# Notes : UBUNTU 13 USERS
+# ---------------
+# The OculusConfigUtil does not currently support Ubuntu 13
+# out of the box. If you see an error similar to this upon
+# launching OculusConfigUtil:
+#
+# "error while loading shared libraries: libudev.so.0"
+#
+# Then please try the following solution, until we officially
+# support Ubuntu 13:
+#
+# cd /lib/x86_64-linux-gnu/
+# sudo ln -sf libudev.so.1 libudev.so.0
+#
+#############################################################################
+
+echo "Installing OculusVR Rift udev rules file..."
+sudo cp ./LibOVR/90-oculus.rules /lib/udev/rules.d
+echo "Installing libudev..."
+sudo apt-get install libudev-dev
+echo "Installing libxext..."
+sudo apt-get install libxext-dev
+echo "Installing mesa-common..."
+sudo apt-get install mesa-common-dev
+echo "Installing freeglut3..."
+sudo apt-get install freeglut3-dev
+echo "Installing Xinerama..."
+sudo apt-get install libxinerama-dev
+echo "Installing Xrandr..."
+sudo apt-get install libxrandr-dev
+echo "Installing XFree86"
+sudo apt-get install libxxf86vm-dev
+echo "Installation complete"
+
diff --git a/LibOVR/90-oculus.rules b/LibOVR/90-oculus.rules
new file mode 100644
index 0000000..56bccdb
--- /dev/null
+++ b/LibOVR/90-oculus.rules
@@ -0,0 +1,2 @@
+# Oculus HID Sensor naming and permissioning
+KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2833", MODE="0666"
diff --git a/LibOVR/Makefile b/LibOVR/Makefile
new file mode 100644
index 0000000..f939cec
--- /dev/null
+++ b/LibOVR/Makefile
@@ -0,0 +1,278 @@
+#############################################################################
+#
+# Filename : Makefile
+# Content : Makefile for building linux version of: libovr
+# Created : 2013
+# Authors : Simon Hallam and Peter Giokaris
+# Copyright : Copyright 2013 OculusVR, Inc. All Rights Reserved
+# Instruction : The g++ compiler and stdndard lib packages need to be
+# installed on the system. Navigate in a shell to the
+# directory where this Makefile is located and enter:
+#
+# make builds the release version for the
+# current architechture
+# make clean delete intermediate release object files
+# and the library file
+# make DEBUG=1 builds the debug version for the current
+# architechture
+# make clean DEBUG=1 deletes intermediate debug object files
+# and the library file
+#
+# Output : Relative to the directory this Makefile lives in, libraries
+# are built at the following locations depending upon the
+# architechture of the system you are running:
+#
+# ./Lib/Linux/Debug/i386/libovr.a
+# ./Lib/Linux/Debug/x86_64/libovr.a
+# ./Lib/Linux/Release/i386/libovr.a
+# ./Lib/Linux/Release/x86_64/libovr.a
+#
+#############################################################################
+
+####### Detect system architecture
+
+SYSARCH = i386
+ifeq ($(shell uname -m),x86_64)
+SYSARCH = x86_64
+endif
+
+####### Compiler, tools and options
+
+CXX = g++
+LINK = ar rvs
+DELETEFILE = rm -f
+
+####### Detect debug or release
+
+DEBUG = 0
+ifeq ($(DEBUG), 1)
+ CXXFLAGS = -pipe -fPIC -DDEBUG -DOVR_BUILD_DEBUG -g
+ RELEASETYPE = Debug
+else
+ CXXFLAGS = -pipe -fPIC -O2
+ RELEASETYPE = Release
+endif
+
+####### Paths
+
+LIBOVRPATH = .
+3RDPARTYPATH = ../3rdParty
+INCPATH = -I. -I.. -I$(LIBOVRPATH)/Include -I$(LIBOVRPATH)/Src
+OBJPATH = ./Obj/Linux/$(RELEASETYPE)/$(SYSARCH)
+CXXBUILD = $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $(OBJPATH)/
+
+####### Files
+
+TARGET = ./Lib/Linux/$(RELEASETYPE)/$(SYSARCH)/libovr.a
+
+OBJECTS = $(OBJPATH)/OVR_CAPI.o \
+ $(OBJPATH)/CAPI_DistortionRenderer.o \
+ $(OBJPATH)/CAPI_GL_DistortionRenderer.o \
+ $(OBJPATH)/CAPI_GL_Util.o \
+ $(OBJPATH)/CAPI_FrameTimeManager.o \
+ $(OBJPATH)/CAPI_GlobalState.o \
+ $(OBJPATH)/CAPI_HMDRenderState.o \
+ $(OBJPATH)/CAPI_HMDState.o \
+ $(OBJPATH)/OVR_DeviceHandle.o \
+ $(OBJPATH)/OVR_DeviceImpl.o \
+ $(OBJPATH)/OVR_JSON.o \
+ $(OBJPATH)/OVR_LatencyTestImpl.o \
+ $(OBJPATH)/OVR_Profile.o \
+ $(OBJPATH)/OVR_Linux_SensorDevice.o\
+ $(OBJPATH)/OVR_SensorCalibration.o\
+ $(OBJPATH)/OVR_SensorFilter.o\
+ $(OBJPATH)/OVR_SensorFusion.o\
+ $(OBJPATH)/OVR_SensorImpl.o \
+ $(OBJPATH)/OVR_Sensor2Impl.o \
+ $(OBJPATH)/OVR_SensorImpl_Common.o \
+ $(OBJPATH)/OVR_SensorTimeFilter.o \
+ $(OBJPATH)/OVR_Stereo.o \
+ $(OBJPATH)/OVR_ThreadCommandQueue.o \
+ $(OBJPATH)/OVR_Alg.o \
+ $(OBJPATH)/OVR_Allocator.o \
+ $(OBJPATH)/OVR_Atomic.o \
+ $(OBJPATH)/OVR_File.o \
+ $(OBJPATH)/OVR_FileFILE.o \
+ $(OBJPATH)/OVR_Log.o \
+ $(OBJPATH)/OVR_Math.o \
+ $(OBJPATH)/OVR_Recording.o \
+ $(OBJPATH)/OVR_RefCount.o \
+ $(OBJPATH)/OVR_Std.o \
+ $(OBJPATH)/OVR_String.o \
+ $(OBJPATH)/OVR_String_FormatUtil.o \
+ $(OBJPATH)/OVR_String_PathUtil.o \
+ $(OBJPATH)/OVR_SysFile.o \
+ $(OBJPATH)/OVR_System.o \
+ $(OBJPATH)/OVR_Timer.o \
+ $(OBJPATH)/OVR_UTF8Util.o \
+ $(OBJPATH)/Util_LatencyTest.o \
+ $(OBJPATH)/Util_LatencyTest2.o \
+ $(OBJPATH)/Util_Render_Stereo.o \
+ $(OBJPATH)/OVR_ThreadsPthread.o \
+ $(OBJPATH)/OVR_Linux_HIDDevice.o \
+ $(OBJPATH)/OVR_Linux_SensorDevice.o \
+ $(OBJPATH)/OVR_Linux_DeviceManager.o \
+ $(OBJPATH)/OVR_Linux_HMDDevice.o \
+ $(OBJPATH)/edid.o
+
+####### Rules
+
+all: $(TARGET)
+
+$(TARGET): $(OBJECTS)
+ $(LINK) $(TARGET) $(OBJECTS)
+
+$(OBJPATH)/OVR_CAPI.o: $(LIBOVRPATH)/Src/OVR_CAPI.cpp
+ $(CXXBUILD)OVR_CAPI.o $(LIBOVRPATH)/Src/OVR_CAPI.cpp
+
+$(OBJPATH)/CAPI_DistortionRenderer.o: $(LIBOVRPATH)/Src/CAPI/CAPI_DistortionRenderer.cpp
+ $(CXXBUILD)CAPI_DistortionRenderer.o $(LIBOVRPATH)/Src/CAPI/CAPI_DistortionRenderer.cpp
+
+$(OBJPATH)/CAPI_GL_DistortionRenderer.o: $(LIBOVRPATH)/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp
+ $(CXXBUILD)CAPI_GL_DistortionRenderer.o $(LIBOVRPATH)/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp
+
+$(OBJPATH)/CAPI_GL_Util.o: $(LIBOVRPATH)/Src/CAPI/GL/CAPI_GL_Util.cpp
+ $(CXXBUILD)CAPI_GL_Util.o $(LIBOVRPATH)/Src/CAPI/GL/CAPI_GL_Util.cpp
+
+$(OBJPATH)/CAPI_FrameTimeManager.o: $(LIBOVRPATH)/Src/CAPI/CAPI_FrameTimeManager.cpp
+ $(CXXBUILD)CAPI_FrameTimeManager.o $(LIBOVRPATH)/Src/CAPI/CAPI_FrameTimeManager.cpp
+
+$(OBJPATH)/CAPI_GlobalState.o: $(LIBOVRPATH)/Src/CAPI/CAPI_GlobalState.cpp
+ $(CXXBUILD)CAPI_GlobalState.o $(LIBOVRPATH)/Src/CAPI/CAPI_GlobalState.cpp
+
+$(OBJPATH)/CAPI_HMDRenderState.o: $(LIBOVRPATH)/Src/CAPI/CAPI_HMDRenderState.cpp
+ $(CXXBUILD)CAPI_HMDRenderState.o $(LIBOVRPATH)/Src/CAPI/CAPI_HMDRenderState.cpp
+
+$(OBJPATH)/CAPI_HMDState.o: $(LIBOVRPATH)/Src/CAPI/CAPI_HMDState.cpp
+ $(CXXBUILD)CAPI_HMDState.o $(LIBOVRPATH)/Src/CAPI/CAPI_HMDState.cpp
+
+$(OBJPATH)/OVR_DeviceHandle.o: $(LIBOVRPATH)/Src/OVR_DeviceHandle.cpp
+ $(CXXBUILD)OVR_DeviceHandle.o $(LIBOVRPATH)/Src/OVR_DeviceHandle.cpp
+
+$(OBJPATH)/OVR_DeviceImpl.o: $(LIBOVRPATH)/Src/OVR_DeviceImpl.cpp
+ $(CXXBUILD)OVR_DeviceImpl.o $(LIBOVRPATH)/Src/OVR_DeviceImpl.cpp
+
+$(OBJPATH)/OVR_JSON.o: $(LIBOVRPATH)/Src/OVR_JSON.cpp
+ $(CXXBUILD)OVR_JSON.o $(LIBOVRPATH)/Src/OVR_JSON.cpp
+
+$(OBJPATH)/OVR_LatencyTestImpl.o: $(LIBOVRPATH)/Src/OVR_LatencyTestImpl.cpp
+ $(CXXBUILD)OVR_LatencyTestImpl.o $(LIBOVRPATH)/Src/OVR_LatencyTestImpl.cpp
+
+$(OBJPATH)/OVR_Profile.o: $(LIBOVRPATH)/Src/OVR_Profile.cpp
+ $(CXXBUILD)OVR_Profile.o $(LIBOVRPATH)/Src/OVR_Profile.cpp
+
+$(OBJPATH)/OVR_SensorDevice.o: $(LIBOVRPATH)/Src/OVR_Linux_SensorDevice.cpp
+ $(CXXBUILD)OVR_Linux_SensorDevice.o $(LIBOVRPATH)/Src/OVR_Linux_SensorDevice.cpp
+
+$(OBJPATH)/OVR_SensorCalibration.o: $(LIBOVRPATH)/Src/OVR_SensorCalibration.cpp
+ $(CXXBUILD)OVR_SensorCalibration.o $(LIBOVRPATH)/Src/OVR_SensorCalibration.cpp
+
+$(OBJPATH)/OVR_SensorFilter.o: $(LIBOVRPATH)/Src/OVR_SensorFilter.cpp
+ $(CXXBUILD)OVR_SensorFilter.o $(LIBOVRPATH)/Src/OVR_SensorFilter.cpp
+
+$(OBJPATH)/OVR_SensorFusion.o: $(LIBOVRPATH)/Src/OVR_SensorFusion.cpp
+ $(CXXBUILD)OVR_SensorFusion.o $(LIBOVRPATH)/Src/OVR_SensorFusion.cpp
+
+$(OBJPATH)/OVR_SensorImpl.o: $(LIBOVRPATH)/Src/OVR_SensorImpl.cpp
+ $(CXXBUILD)OVR_SensorImpl.o $(LIBOVRPATH)/Src/OVR_SensorImpl.cpp
+
+$(OBJPATH)/OVR_Sensor2Impl.o: $(LIBOVRPATH)/Src/OVR_Sensor2Impl.cpp
+ $(CXXBUILD)OVR_Sensor2Impl.o $(LIBOVRPATH)/Src/OVR_Sensor2Impl.cpp
+
+$(OBJPATH)/OVR_SensorImpl_Common.o: $(LIBOVRPATH)/Src/OVR_SensorImpl_Common.cpp
+ $(CXXBUILD)OVR_SensorImpl_Common.o $(LIBOVRPATH)/Src/OVR_SensorImpl_Common.cpp
+
+$(OBJPATH)/OVR_SensorTimeFilter.o: $(LIBOVRPATH)/Src/OVR_SensorTimeFilter.cpp
+ $(CXXBUILD)OVR_SensorTimeFilter.o $(LIBOVRPATH)/Src/OVR_SensorTimeFilter.cpp
+
+$(OBJPATH)/OVR_Stereo.o: $(LIBOVRPATH)/Src/OVR_Stereo.cpp
+ $(CXXBUILD)OVR_Stereo.o $(LIBOVRPATH)/Src/OVR_Stereo.cpp
+
+$(OBJPATH)/OVR_ThreadCommandQueue.o: $(LIBOVRPATH)/Src/OVR_ThreadCommandQueue.cpp
+ $(CXXBUILD)OVR_ThreadCommandQueue.o $(LIBOVRPATH)/Src/OVR_ThreadCommandQueue.cpp
+
+$(OBJPATH)/OVR_Alg.o: $(LIBOVRPATH)/Src/Kernel/OVR_Alg.cpp
+ $(CXXBUILD)OVR_Alg.o $(LIBOVRPATH)/Src/Kernel/OVR_Alg.cpp
+
+$(OBJPATH)/OVR_Allocator.o: $(LIBOVRPATH)/Src/Kernel/OVR_Allocator.cpp
+ $(CXXBUILD)OVR_Allocator.o $(LIBOVRPATH)/Src/Kernel/OVR_Allocator.cpp
+
+$(OBJPATH)/OVR_Atomic.o: $(LIBOVRPATH)/Src/Kernel/OVR_Atomic.cpp
+ $(CXXBUILD)OVR_Atomic.o $(LIBOVRPATH)/Src/Kernel/OVR_Atomic.cpp
+
+$(OBJPATH)/OVR_File.o: $(LIBOVRPATH)/Src/Kernel/OVR_File.cpp
+ $(CXXBUILD)OVR_File.o $(LIBOVRPATH)/Src/Kernel/OVR_File.cpp
+
+$(OBJPATH)/OVR_FileFILE.o: $(LIBOVRPATH)/Src/Kernel/OVR_FileFILE.cpp
+ $(CXXBUILD)OVR_FileFILE.o $(LIBOVRPATH)/Src/Kernel/OVR_FileFILE.cpp
+
+$(OBJPATH)/OVR_Log.o: $(LIBOVRPATH)/Src/Kernel/OVR_Log.cpp
+ $(CXXBUILD)OVR_Log.o $(LIBOVRPATH)/Src/Kernel/OVR_Log.cpp
+
+$(OBJPATH)/OVR_Math.o: $(LIBOVRPATH)/Src/Kernel/OVR_Math.cpp
+ $(CXXBUILD)OVR_Math.o $(LIBOVRPATH)/Src/Kernel/OVR_Math.cpp
+
+$(OBJPATH)/OVR_Recording.o: $(LIBOVRPATH)/Src/OVR_Recording.cpp
+ $(CXXBUILD)OVR_Recording.o $(LIBOVRPATH)/Src/OVR_Recording.cpp
+
+$(OBJPATH)/OVR_RefCount.o: $(LIBOVRPATH)/Src/Kernel/OVR_RefCount.cpp
+ $(CXXBUILD)OVR_RefCount.o $(LIBOVRPATH)/Src/Kernel/OVR_RefCount.cpp
+
+$(OBJPATH)/OVR_Std.o: $(LIBOVRPATH)/Src/Kernel/OVR_Std.cpp
+ $(CXXBUILD)OVR_Std.o $(LIBOVRPATH)/Src/Kernel/OVR_Std.cpp
+
+$(OBJPATH)/OVR_String.o: $(LIBOVRPATH)/Src/Kernel/OVR_String.cpp
+ $(CXXBUILD)OVR_String.o $(LIBOVRPATH)/Src/Kernel/OVR_String.cpp
+
+$(OBJPATH)/OVR_String_FormatUtil.o: $(LIBOVRPATH)/Src/Kernel/OVR_String_FormatUtil.cpp
+ $(CXXBUILD)OVR_String_FormatUtil.o $(LIBOVRPATH)/Src/Kernel/OVR_String_FormatUtil.cpp
+
+$(OBJPATH)/OVR_String_PathUtil.o: $(LIBOVRPATH)/Src/Kernel/OVR_String_PathUtil.cpp
+ $(CXXBUILD)OVR_String_PathUtil.o $(LIBOVRPATH)/Src/Kernel/OVR_String_PathUtil.cpp
+
+$(OBJPATH)/OVR_SysFile.o: $(LIBOVRPATH)/Src/Kernel/OVR_SysFile.cpp
+ $(CXXBUILD)OVR_SysFile.o $(LIBOVRPATH)/Src/Kernel/OVR_SysFile.cpp
+
+$(OBJPATH)/OVR_System.o: $(LIBOVRPATH)/Src/Kernel/OVR_System.cpp
+ $(CXXBUILD)OVR_System.o $(LIBOVRPATH)/Src/Kernel/OVR_System.cpp
+
+$(OBJPATH)/OVR_Timer.o: $(LIBOVRPATH)/Src/Kernel/OVR_Timer.cpp
+ $(CXXBUILD)OVR_Timer.o $(LIBOVRPATH)/Src/Kernel/OVR_Timer.cpp
+
+$(OBJPATH)/OVR_UTF8Util.o: $(LIBOVRPATH)/Src/Kernel/OVR_UTF8Util.cpp
+ $(CXXBUILD)OVR_UTF8Util.o $(LIBOVRPATH)/Src/Kernel/OVR_UTF8Util.cpp
+
+$(OBJPATH)/Util_LatencyTest.o: $(LIBOVRPATH)/Src/Util/Util_LatencyTest.cpp
+ $(CXXBUILD)Util_LatencyTest.o $(LIBOVRPATH)/Src/Util/Util_LatencyTest.cpp
+
+$(OBJPATH)/Util_LatencyTest2.o: $(LIBOVRPATH)/Src/Util/Util_LatencyTest2.cpp
+ $(CXXBUILD)Util_LatencyTest2.o $(LIBOVRPATH)/Src/Util/Util_LatencyTest2.cpp
+
+$(OBJPATH)/Util_Render_Stereo.o: $(LIBOVRPATH)/Src/Util/Util_Render_Stereo.cpp
+ $(CXXBUILD)Util_Render_Stereo.o $(LIBOVRPATH)/Src/Util/Util_Render_Stereo.cpp
+
+$(OBJPATH)/OVR_ThreadsPthread.o: $(LIBOVRPATH)/Src/Kernel/OVR_ThreadsPthread.cpp
+ $(CXXBUILD)OVR_ThreadsPthread.o $(LIBOVRPATH)/Src/Kernel/OVR_ThreadsPthread.cpp
+
+$(OBJPATH)/OVR_Linux_HIDDevice.o: $(LIBOVRPATH)/Src/OVR_Linux_HIDDevice.cpp
+ $(CXXBUILD)OVR_Linux_HIDDevice.o $(LIBOVRPATH)/Src/OVR_Linux_HIDDevice.cpp
+
+$(OBJPATH)/OVR_Linux_SensorDevice.o: $(LIBOVRPATH)/Src/OVR_Linux_SensorDevice.cpp
+ $(CXXBUILD)OVR_Linux_SensorDevice.o $(LIBOVRPATH)/Src/OVR_Linux_SensorDevice.cpp
+
+$(OBJPATH)/OVR_Linux_DeviceManager.o: $(LIBOVRPATH)/Src/OVR_Linux_DeviceManager.cpp
+ $(CXXBUILD)OVR_Linux_DeviceManager.o $(LIBOVRPATH)/Src/OVR_Linux_DeviceManager.cpp
+
+$(OBJPATH)/OVR_Linux_HMDDevice.o: $(LIBOVRPATH)/Src/OVR_Linux_HMDDevice.cpp
+ $(CXXBUILD)OVR_Linux_HMDDevice.o $(LIBOVRPATH)/Src/OVR_Linux_HMDDevice.cpp
+
+$(OBJPATH)/tinyxml2.o: $(3RDPARTYPATH)/TinyXml/tinyxml2.cpp
+ $(CXXBUILD)tinyxml2.o $(3RDPARTYPATH)/TinyXml/tinyxml2.cpp
+
+$(OBJPATH)/edid.o: $(3RDPARTYPATH)/EDID/edid.cpp
+ $(CXXBUILD)edid.o $(3RDPARTYPATH)/EDID/edid.cpp
+
+clean:
+ -$(DELETEFILE) $(OBJECTS)
+ -$(DELETEFILE) $(TARGET)
+
diff --git a/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp b/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp
new file mode 100644
index 0000000..da483d5
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp
@@ -0,0 +1,787 @@
+/************************************************************************************
+
+Filename : OVR_ThreadsPthread.cpp
+Content :
+Created :
+Notes :
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "OVR_Threads.h"
+#include "OVR_Hash.h"
+
+#ifdef OVR_ENABLE_THREADS
+
+#include "OVR_Timer.h"
+#include "OVR_Log.h"
+
+#include <pthread.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <errno.h>
+
+
+namespace OVR {
+
+// ***** Mutex implementation
+
+
+// *** Internal Mutex implementation structure
+
+class MutexImpl : public NewOverrideBase
+{
+ // System mutex or semaphore
+ pthread_mutex_t SMutex;
+ bool Recursive;
+ unsigned LockCount;
+ pthread_t LockedBy;
+
+ friend class WaitConditionImpl;
+
+public:
+ // Constructor/destructor
+ MutexImpl(Mutex* pmutex, bool recursive = 1);
+ ~MutexImpl();
+
+ // Locking functions
+ void DoLock();
+ bool TryLock();
+ void Unlock(Mutex* pmutex);
+ // Returns 1 if the mutes is currently locked
+ bool IsLockedByAnotherThread(Mutex* pmutex);
+ bool IsSignaled() const;
+};
+
+pthread_mutexattr_t Lock::RecursiveAttr;
+bool Lock::RecursiveAttrInit = 0;
+
+// *** Constructor/destructor
+MutexImpl::MutexImpl(Mutex* pmutex, bool recursive)
+{
+ OVR_UNUSED(pmutex);
+ Recursive = recursive;
+ LockCount = 0;
+
+ if (Recursive)
+ {
+ if (!Lock::RecursiveAttrInit)
+ {
+ pthread_mutexattr_init(&Lock::RecursiveAttr);
+ pthread_mutexattr_settype(&Lock::RecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
+ Lock::RecursiveAttrInit = 1;
+ }
+
+ pthread_mutex_init(&SMutex, &Lock::RecursiveAttr);
+ }
+ else
+ pthread_mutex_init(&SMutex, 0);
+}
+
+MutexImpl::~MutexImpl()
+{
+ pthread_mutex_destroy(&SMutex);
+}
+
+
+// Lock and try lock
+void MutexImpl::DoLock()
+{
+ while (pthread_mutex_lock(&SMutex))
+ ;
+ LockCount++;
+ LockedBy = pthread_self();
+}
+
+bool MutexImpl::TryLock()
+{
+ if (!pthread_mutex_trylock(&SMutex))
+ {
+ LockCount++;
+ LockedBy = pthread_self();
+ return 1;
+ }
+
+ return 0;
+}
+
+void MutexImpl::Unlock(Mutex* pmutex)
+{
+ OVR_UNUSED(pmutex);
+ OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0);
+
+ unsigned lockCount;
+ LockCount--;
+ lockCount = LockCount;
+
+ pthread_mutex_unlock(&SMutex);
+}
+
+bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex)
+{
+ OVR_UNUSED(pmutex);
+ // There could be multiple interpretations of IsLocked with respect to current thread
+ if (LockCount == 0)
+ return 0;
+ if (pthread_self() != LockedBy)
+ return 1;
+ return 0;
+}
+
+bool MutexImpl::IsSignaled() const
+{
+ // An mutex is signaled if it is not locked ANYWHERE
+ // Note that this is different from IsLockedByAnotherThread function,
+ // that takes current thread into account
+ return LockCount == 0;
+}
+
+
+// *** Actual Mutex class implementation
+
+Mutex::Mutex(bool recursive)
+{
+ // NOTE: RefCount mode already thread-safe for all waitables.
+ pImpl = new MutexImpl(this, recursive);
+}
+
+Mutex::~Mutex()
+{
+ delete pImpl;
+}
+
+// Lock and try lock
+void Mutex::DoLock()
+{
+ pImpl->DoLock();
+}
+bool Mutex::TryLock()
+{
+ return pImpl->TryLock();
+}
+void Mutex::Unlock()
+{
+ pImpl->Unlock(this);
+}
+bool Mutex::IsLockedByAnotherThread()
+{
+ return pImpl->IsLockedByAnotherThread(this);
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Event
+
+bool Event::Wait(unsigned delay)
+{
+ Mutex::Locker lock(&StateMutex);
+
+ // Do the correct amount of waiting
+ if (delay == OVR_WAIT_INFINITE)
+ {
+ while(!State)
+ StateWaitCondition.Wait(&StateMutex);
+ }
+ else if (delay)
+ {
+ if (!State)
+ StateWaitCondition.Wait(&StateMutex, delay);
+ }
+
+ bool state = State;
+ // Take care of temporary 'pulsing' of a state
+ if (Temporary)
+ {
+ Temporary = false;
+ State = false;
+ }
+ return state;
+}
+
+void Event::updateState(bool newState, bool newTemp, bool mustNotify)
+{
+ Mutex::Locker lock(&StateMutex);
+ State = newState;
+ Temporary = newTemp;
+ if (mustNotify)
+ StateWaitCondition.NotifyAll();
+}
+
+
+
+// ***** Wait Condition Implementation
+
+// Internal implementation class
+class WaitConditionImpl : public NewOverrideBase
+{
+ pthread_mutex_t SMutex;
+ pthread_cond_t Condv;
+
+public:
+
+ // Constructor/destructor
+ WaitConditionImpl();
+ ~WaitConditionImpl();
+
+ // Release mutex and wait for condition. The mutex is re-aqured after the wait.
+ bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE);
+
+ // Notify a condition, releasing at one object waiting
+ void Notify();
+ // Notify a condition, releasing all objects waiting
+ void NotifyAll();
+};
+
+
+WaitConditionImpl::WaitConditionImpl()
+{
+ pthread_mutex_init(&SMutex, 0);
+ pthread_cond_init(&Condv, 0);
+}
+
+WaitConditionImpl::~WaitConditionImpl()
+{
+ pthread_mutex_destroy(&SMutex);
+ pthread_cond_destroy(&Condv);
+}
+
+bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay)
+{
+ bool result = 1;
+ unsigned lockCount = pmutex->pImpl->LockCount;
+
+ // Mutex must have been locked
+ if (lockCount == 0)
+ return 0;
+
+ pthread_mutex_lock(&SMutex);
+
+ // Finally, release a mutex or semaphore
+ if (pmutex->pImpl->Recursive)
+ {
+ // Release the recursive mutex N times
+ pmutex->pImpl->LockCount = 0;
+ for(unsigned i=0; i<lockCount; i++)
+ pthread_mutex_unlock(&pmutex->pImpl->SMutex);
+ }
+ else
+ {
+ pmutex->pImpl->LockCount = 0;
+ pthread_mutex_unlock(&pmutex->pImpl->SMutex);
+ }
+
+ // Note that there is a gap here between mutex.Unlock() and Wait().
+ // The other mutex protects this gap.
+
+ if (delay == OVR_WAIT_INFINITE)
+ pthread_cond_wait(&Condv,&SMutex);
+ else
+ {
+ timespec ts;
+
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+
+ ts.tv_sec = tv.tv_sec + (delay / 1000);
+ ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000;
+
+ if (ts.tv_nsec > 999999999)
+ {
+ ts.tv_sec++;
+ ts.tv_nsec -= 1000000000;
+ }
+ int r = pthread_cond_timedwait(&Condv,&SMutex, &ts);
+ OVR_ASSERT(r == 0 || r == ETIMEDOUT);
+ if (r)
+ result = 0;
+ }
+
+ pthread_mutex_unlock(&SMutex);
+
+ // Re-aquire the mutex
+ for(unsigned i=0; i<lockCount; i++)
+ pmutex->DoLock();
+
+ // Return the result
+ return result;
+}
+
+// Notify a condition, releasing the least object in a queue
+void WaitConditionImpl::Notify()
+{
+ pthread_mutex_lock(&SMutex);
+ pthread_cond_signal(&Condv);
+ pthread_mutex_unlock(&SMutex);
+}
+
+// Notify a condition, releasing all objects waiting
+void WaitConditionImpl::NotifyAll()
+{
+ pthread_mutex_lock(&SMutex);
+ pthread_cond_broadcast(&Condv);
+ pthread_mutex_unlock(&SMutex);
+}
+
+
+
+// *** Actual implementation of WaitCondition
+
+WaitCondition::WaitCondition()
+{
+ pImpl = new WaitConditionImpl;
+}
+WaitCondition::~WaitCondition()
+{
+ delete pImpl;
+}
+
+bool WaitCondition::Wait(Mutex *pmutex, unsigned delay)
+{
+ return pImpl->Wait(pmutex, delay);
+}
+// Notification
+void WaitCondition::Notify()
+{
+ pImpl->Notify();
+}
+void WaitCondition::NotifyAll()
+{
+ pImpl->NotifyAll();
+}
+
+
+// ***** Current thread
+
+// Per-thread variable
+/*
+static __thread Thread* pCurrentThread = 0;
+
+// Static function to return a pointer to the current thread
+void Thread::InitCurrentThread(Thread *pthread)
+{
+ pCurrentThread = pthread;
+}
+
+// Static function to return a pointer to the current thread
+Thread* Thread::GetThread()
+{
+ return pCurrentThread;
+}
+*/
+
+
+// *** Thread constructors.
+
+Thread::Thread(UPInt stackSize, int processor)
+{
+ // NOTE: RefCount mode already thread-safe for all Waitable objects.
+ CreateParams params;
+ params.stackSize = stackSize;
+ params.processor = processor;
+ Init(params);
+}
+
+Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize,
+ int processor, Thread::ThreadState initialState)
+{
+ CreateParams params(threadFunction, userHandle, stackSize, processor, initialState);
+ Init(params);
+}
+
+Thread::Thread(const CreateParams& params)
+{
+ Init(params);
+}
+
+void Thread::Init(const CreateParams& params)
+{
+ // Clear the variables
+ ThreadFlags = 0;
+ ThreadHandle = 0;
+ ExitCode = 0;
+ SuspendCount = 0;
+ StackSize = params.stackSize;
+ Processor = params.processor;
+ Priority = params.priority;
+
+ // Clear Function pointers
+ ThreadFunction = params.threadFunction;
+ UserHandle = params.userHandle;
+ if (params.initialState != NotRunning)
+ Start(params.initialState);
+}
+
+Thread::~Thread()
+{
+ // Thread should not running while object is being destroyed,
+ // this would indicate ref-counting issue.
+ //OVR_ASSERT(IsRunning() == 0);
+
+ // Clean up thread.
+ ThreadHandle = 0;
+}
+
+
+
+// *** Overridable User functions.
+
+// Default Run implementation
+int Thread::Run()
+{
+ // Call pointer to function, if available.
+ return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0;
+}
+void Thread::OnExit()
+{
+}
+
+
+// Finishes the thread and releases internal reference to it.
+void Thread::FinishAndRelease()
+{
+ // Note: thread must be US.
+ ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED);
+ ThreadFlags |= OVR_THREAD_FINISHED;
+
+ // Release our reference; this is equivalent to 'delete this'
+ // from the point of view of our thread.
+ Release();
+}
+
+
+
+// *** ThreadList - used to track all created threads
+
+class ThreadList : public NewOverrideBase
+{
+ //------------------------------------------------------------------------
+ struct ThreadHashOp
+ {
+ size_t operator()(const Thread* ptr)
+ {
+ return (((size_t)ptr) >> 6) ^ (size_t)ptr;
+ }
+ };
+
+ HashSet<Thread*, ThreadHashOp> ThreadSet;
+ Mutex ThreadMutex;
+ WaitCondition ThreadsEmpty;
+ // Track the root thread that created us.
+ pthread_t RootThreadId;
+
+ static ThreadList* volatile pRunningThreads;
+
+ void addThread(Thread *pthread)
+ {
+ Mutex::Locker lock(&ThreadMutex);
+ ThreadSet.Add(pthread);
+ }
+
+ void removeThread(Thread *pthread)
+ {
+ Mutex::Locker lock(&ThreadMutex);
+ ThreadSet.Remove(pthread);
+ if (ThreadSet.GetSize() == 0)
+ ThreadsEmpty.Notify();
+ }
+
+ void finishAllThreads()
+ {
+ // Only original root thread can call this.
+ OVR_ASSERT(pthread_self() == RootThreadId);
+
+ Mutex::Locker lock(&ThreadMutex);
+ while (ThreadSet.GetSize() != 0)
+ ThreadsEmpty.Wait(&ThreadMutex);
+ }
+
+public:
+
+ ThreadList()
+ {
+ RootThreadId = pthread_self();
+ }
+ ~ThreadList() { }
+
+
+ static void AddRunningThread(Thread *pthread)
+ {
+ // Non-atomic creation ok since only the root thread
+ if (!pRunningThreads)
+ {
+ pRunningThreads = new ThreadList;
+ OVR_ASSERT(pRunningThreads);
+ }
+ pRunningThreads->addThread(pthread);
+ }
+
+ // NOTE: 'pthread' might be a dead pointer when this is
+ // called so it should not be accessed; it is only used
+ // for removal.
+ static void RemoveRunningThread(Thread *pthread)
+ {
+ OVR_ASSERT(pRunningThreads);
+ pRunningThreads->removeThread(pthread);
+ }
+
+ static void FinishAllThreads()
+ {
+ // This is ok because only root thread can wait for other thread finish.
+ if (pRunningThreads)
+ {
+ pRunningThreads->finishAllThreads();
+ delete pRunningThreads;
+ pRunningThreads = 0;
+ }
+ }
+};
+
+// By default, we have no thread list.
+ThreadList* volatile ThreadList::pRunningThreads = 0;
+
+
+// FinishAllThreads - exposed publicly in Thread.
+void Thread::FinishAllThreads()
+{
+ ThreadList::FinishAllThreads();
+}
+
+// *** Run override
+
+int Thread::PRun()
+{
+ // Suspend us on start, if requested
+ if (ThreadFlags & OVR_THREAD_START_SUSPENDED)
+ {
+ Suspend();
+ ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED;
+ }
+
+ // Call the virtual run function
+ ExitCode = Run();
+ return ExitCode;
+}
+
+
+
+
+// *** User overridables
+
+bool Thread::GetExitFlag() const
+{
+ return (ThreadFlags & OVR_THREAD_EXIT) != 0;
+}
+
+void Thread::SetExitFlag(bool exitFlag)
+{
+ // The below is atomic since ThreadFlags is AtomicInt.
+ if (exitFlag)
+ ThreadFlags |= OVR_THREAD_EXIT;
+ else
+ ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT;
+}
+
+
+// Determines whether the thread was running and is now finished
+bool Thread::IsFinished() const
+{
+ return (ThreadFlags & OVR_THREAD_FINISHED) != 0;
+}
+// Determines whether the thread is suspended
+bool Thread::IsSuspended() const
+{
+ return SuspendCount > 0;
+}
+// Returns current thread state
+Thread::ThreadState Thread::GetThreadState() const
+{
+ if (IsSuspended())
+ return Suspended;
+ if (ThreadFlags & OVR_THREAD_STARTED)
+ return Running;
+ return NotRunning;
+}
+/*
+static const char* mapsched_policy(int policy)
+{
+ switch(policy)
+ {
+ case SCHED_OTHER:
+ return "SCHED_OTHER";
+ case SCHED_RR:
+ return "SCHED_RR";
+ case SCHED_FIFO:
+ return "SCHED_FIFO";
+
+ }
+ return "UNKNOWN";
+}
+ int policy;
+ sched_param sparam;
+ pthread_getschedparam(pthread_self(), &policy, &sparam);
+ int max_prior = sched_get_priority_max(policy);
+ int min_prior = sched_get_priority_min(policy);
+ printf(" !!!! policy: %s, priority: %d, max priority: %d, min priority: %d\n", mapsched_policy(policy), sparam.sched_priority, max_prior, min_prior);
+#include <stdio.h>
+*/
+// ***** Thread management
+
+// The actual first function called on thread start
+void* Thread_PthreadStartFn(void* phandle)
+{
+ Thread* pthread = (Thread*)phandle;
+ int result = pthread->PRun();
+ // Signal the thread as done and release it atomically.
+ pthread->FinishAndRelease();
+ // At this point Thread object might be dead; however we can still pass
+ // it to RemoveRunningThread since it is only used as a key there.
+ ThreadList::RemoveRunningThread(pthread);
+ return reinterpret_cast<void*>(result);
+}
+
+int Thread::InitAttr = 0;
+pthread_attr_t Thread::Attr;
+
+/* static */
+int Thread::GetOSPriority(ThreadPriority p)
+//static inline int MapToSystemPrority(Thread::ThreadPriority p)
+{
+ OVR_UNUSED(p);
+ return -1;
+}
+
+bool Thread::Start(ThreadState initialState)
+{
+ if (initialState == NotRunning)
+ return 0;
+ if (GetThreadState() != NotRunning)
+ {
+ OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this));
+ return 0;
+ }
+
+ if (!InitAttr)
+ {
+ pthread_attr_init(&Attr);
+ pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setstacksize(&Attr, 128 * 1024);
+ sched_param sparam;
+ sparam.sched_priority = Thread::GetOSPriority(NormalPriority);
+ pthread_attr_setschedparam(&Attr, &sparam);
+ InitAttr = 1;
+ }
+
+ ExitCode = 0;
+ SuspendCount = 0;
+ ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED;
+
+ // AddRef to us until the thread is finished
+ AddRef();
+ ThreadList::AddRunningThread(this);
+
+ int result;
+ if (StackSize != 128 * 1024 || Priority != NormalPriority)
+ {
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setstacksize(&attr, StackSize);
+ sched_param sparam;
+ sparam.sched_priority = Thread::GetOSPriority(Priority);
+ pthread_attr_setschedparam(&attr, &sparam);
+ result = pthread_create(&ThreadHandle, &attr, Thread_PthreadStartFn, this);
+ pthread_attr_destroy(&attr);
+ }
+ else
+ result = pthread_create(&ThreadHandle, &Attr, Thread_PthreadStartFn, this);
+
+ if (result)
+ {
+ ThreadFlags = 0;
+ Release();
+ ThreadList::RemoveRunningThread(this);
+ return 0;
+ }
+ return 1;
+}
+
+
+// Suspend the thread until resumed
+bool Thread::Suspend()
+{
+ OVR_DEBUG_LOG(("Thread::Suspend - cannot suspend threads on this system"));
+ return 0;
+}
+
+// Resumes currently suspended thread
+bool Thread::Resume()
+{
+ return 0;
+}
+
+
+// Quits with an exit code
+void Thread::Exit(int exitCode)
+{
+ // Can only exist the current thread
+ // if (GetThread() != this)
+ // return;
+
+ // Call the virtual OnExit function
+ OnExit();
+
+ // Signal this thread object as done and release it's references.
+ FinishAndRelease();
+ ThreadList::RemoveRunningThread(this);
+
+ pthread_exit(reinterpret_cast<void*>(exitCode));
+}
+
+ThreadId GetCurrentThreadId()
+{
+ return (void*)pthread_self();
+}
+
+// *** Sleep functions
+
+/* static */
+bool Thread::Sleep(unsigned secs)
+{
+ sleep(secs);
+ return 1;
+}
+/* static */
+bool Thread::MSleep(unsigned msecs)
+{
+ usleep(msecs*1000);
+ return 1;
+}
+
+/* static */
+int Thread::GetCPUCount()
+{
+ return 1;
+}
+
+}
+
+#endif // OVR_ENABLE_THREADS
diff --git a/LibOVR/Src/OVR_Linux_DeviceManager.cpp b/LibOVR/Src/OVR_Linux_DeviceManager.cpp
new file mode 100644
index 0000000..f1c4278
--- /dev/null
+++ b/LibOVR/Src/OVR_Linux_DeviceManager.cpp
@@ -0,0 +1,331 @@
+/************************************************************************************
+
+Filename : OVR_Linux_DeviceManager.h
+Content : Linux implementation of DeviceManager.
+Created :
+Authors :
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#include "OVR_Linux_DeviceManager.h"
+
+// Sensor & HMD Factories
+#include "OVR_LatencyTestImpl.h"
+#include "OVR_SensorImpl.h"
+#include "OVR_Linux_HIDDevice.h"
+#include "OVR_Linux_HMDDevice.h"
+
+#include "Kernel/OVR_Timer.h"
+#include "Kernel/OVR_Std.h"
+#include "Kernel/OVR_Log.h"
+
+namespace OVR { namespace Linux {
+
+
+//-------------------------------------------------------------------------------------
+// **** Linux::DeviceManager
+
+DeviceManager::DeviceManager()
+{
+}
+
+DeviceManager::~DeviceManager()
+{
+}
+
+bool DeviceManager::Initialize(DeviceBase*)
+{
+ if (!DeviceManagerImpl::Initialize(0))
+ return false;
+
+ pThread = *new DeviceManagerThread();
+ if (!pThread || !pThread->Start())
+ return false;
+
+ // Wait for the thread to be fully up and running.
+ pThread->StartupEvent.Wait();
+
+ // Do this now that we know the thread's run loop.
+ HidDeviceManager = *HIDDeviceManager::CreateInternal(this);
+
+ pCreateDesc->pDevice = this;
+ LogText("OVR::DeviceManager - initialized.\n");
+ return true;
+}
+
+void DeviceManager::Shutdown()
+{
+ LogText("OVR::DeviceManager - shutting down.\n");
+
+ // Set Manager shutdown marker variable; this prevents
+ // any existing DeviceHandle objects from accessing device.
+ pCreateDesc->pLock->pManager = 0;
+
+ // Push for thread shutdown *WITH NO WAIT*.
+ // This will have the following effect:
+ // - Exit command will get enqueued, which will be executed later on the thread itself.
+ // - Beyond this point, this DeviceManager object may be deleted by our caller.
+ // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
+ // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
+ // after pManager is null.
+ // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
+ // reference to the thread object.
+ pThread->PushExitCommand(false);
+ pThread.Clear();
+
+ DeviceManagerImpl::Shutdown();
+}
+
+ThreadCommandQueue* DeviceManager::GetThreadQueue()
+{
+ return pThread;
+}
+
+ThreadId DeviceManager::GetThreadId() const
+{
+ return pThread->GetThreadId();
+}
+
+bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_Manager) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ info->Type = Device_Manager;
+ info->Version = 0;
+ info->ProductName = "DeviceManager";
+ info->Manufacturer = "Oculus VR, Inc.";
+ return true;
+}
+
+DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args)
+{
+ // TBD: Can this be avoided in the future, once proper device notification is in place?
+ pThread->PushCall((DeviceManagerImpl*)this,
+ &DeviceManager::EnumerateAllFactoryDevices, true);
+
+ return DeviceManagerImpl::EnumerateDevicesEx(args);
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManager Thread
+
+DeviceManagerThread::DeviceManagerThread()
+ : Thread(ThreadStackSize)
+{
+ int result = pipe(CommandFd);
+ OVR_ASSERT(!result);
+ OVR_UNUSED(result);
+
+ AddSelectFd(NULL, CommandFd[0]);
+}
+
+DeviceManagerThread::~DeviceManagerThread()
+{
+ if (CommandFd[0])
+ {
+ RemoveSelectFd(NULL, CommandFd[0]);
+ close(CommandFd[0]);
+ close(CommandFd[1]);
+ }
+}
+
+bool DeviceManagerThread::AddSelectFd(Notifier* notify, int fd)
+{
+ struct pollfd pfd;
+ pfd.fd = fd;
+ pfd.events = POLLIN|POLLHUP|POLLERR;
+ pfd.revents = 0;
+
+ FdNotifiers.PushBack(notify);
+ PollFds.PushBack(pfd);
+
+ OVR_ASSERT(FdNotifiers.GetSize() == PollFds.GetSize());
+ return true;
+}
+
+bool DeviceManagerThread::RemoveSelectFd(Notifier* notify, int fd)
+{
+ // [0] is reserved for thread commands with notify of null, but we still
+ // can use this function to remove it.
+ for (UPInt i = 0; i < FdNotifiers.GetSize(); i++)
+ {
+ if ((FdNotifiers[i] == notify) && (PollFds[i].fd == fd))
+ {
+ FdNotifiers.RemoveAt(i);
+ PollFds.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+int DeviceManagerThread::Run()
+{
+ ThreadCommand::PopBuffer command;
+
+ SetThreadName("OVR::DeviceManagerThread");
+ LogText("OVR::DeviceManagerThread - running (ThreadId=%p).\n", GetThreadId());
+
+ // Signal to the parent thread that initialization has finished.
+ StartupEvent.SetEvent();
+
+ while(!IsExiting())
+ {
+ // PopCommand will reset event on empty queue.
+ if (PopCommand(&command))
+ {
+ command.Execute();
+ }
+ else
+ {
+ bool commands = 0;
+ do
+ {
+ int waitMs = -1;
+
+ // If devices have time-dependent logic registered, get the longest wait
+ // allowed based on current ticks.
+ if (!TicksNotifiers.IsEmpty())
+ {
+ double timeSeconds = Timer::GetSeconds();
+ unsigned waitAllowed;
+
+ for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++)
+ {
+ waitAllowed = (unsigned)(TicksNotifiers[j]->OnTicks(timeSeconds) * Timer::MsPerSecond);
+ if (waitAllowed < (unsigned)waitMs)
+ waitMs = waitAllowed;
+ }
+ }
+
+ // wait until there is data available on one of the devices or the timeout expires
+ int n = poll(&PollFds[0], PollFds.GetSize(), waitMs);
+
+ if (n > 0)
+ {
+ // Iterate backwards through the list so the ordering will not be
+ // affected if the called object gets removed during the callback
+ // Also, the HID data streams are located toward the back of the list
+ // and servicing them first will allow a disconnect to be handled
+ // and cleaned directly at the device first instead of the general HID monitor
+ for (int i=PollFds.GetSize()-1; i>=0; i--)
+ {
+ if (PollFds[i].revents & POLLERR)
+ {
+ OVR_DEBUG_LOG(("poll: error on [%d]: %d", i, PollFds[i].fd));
+ }
+ else if (PollFds[i].revents & POLLIN)
+ {
+ if (FdNotifiers[i])
+ FdNotifiers[i]->OnEvent(i, PollFds[i].fd);
+ else if (i == 0) // command
+ {
+ char dummy[128];
+ read(PollFds[i].fd, dummy, 128);
+ commands = 1;
+ }
+ }
+
+ if (PollFds[i].revents & POLLHUP)
+ PollFds[i].events = 0;
+
+ if (PollFds[i].revents != 0)
+ {
+ n--;
+ if (n == 0)
+ break;
+ }
+ }
+ }
+ } while (PollFds.GetSize() > 0 && !commands);
+ }
+ }
+
+ LogText("OVR::DeviceManagerThread - exiting (ThreadId=%p).\n", GetThreadId());
+ return 0;
+}
+
+bool DeviceManagerThread::AddTicksNotifier(Notifier* notify)
+{
+ TicksNotifiers.PushBack(notify);
+ return true;
+}
+
+bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify)
+{
+ for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++)
+ {
+ if (TicksNotifiers[i] == notify)
+ {
+ TicksNotifiers.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace Linux
+
+
+//-------------------------------------------------------------------------------------
+// ***** Creation
+
+
+// Creates a new DeviceManager and initializes OVR.
+DeviceManager* DeviceManager::Create()
+{
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<Linux::DeviceManager> manager = *new Linux::DeviceManager;
+
+ if (manager)
+ {
+ if (manager->Initialize(0))
+ {
+ manager->AddFactory(&LatencyTestDeviceFactory::GetInstance());
+ manager->AddFactory(&SensorDeviceFactory::GetInstance());
+ manager->AddFactory(&Linux::HMDDeviceFactory::GetInstance());
+
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+
+ }
+
+ return manager.GetPtr();
+}
+
+
+} // namespace OVR
+
diff --git a/LibOVR/Src/OVR_Linux_DeviceManager.h b/LibOVR/Src/OVR_Linux_DeviceManager.h
new file mode 100644
index 0000000..6532103
--- /dev/null
+++ b/LibOVR/Src/OVR_Linux_DeviceManager.h
@@ -0,0 +1,122 @@
+/************************************************************************************
+
+Filename : OVR_Linux_DeviceManager.h
+Content : Linux-specific DeviceManager header.
+Created :
+Authors :
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#ifndef OVR_Linux_DeviceManager_h
+#define OVR_Linux_DeviceManager_h
+
+#include "OVR_DeviceImpl.h"
+
+#include <unistd.h>
+#include <sys/poll.h>
+
+
+namespace OVR { namespace Linux {
+
+class DeviceManagerThread;
+
+//-------------------------------------------------------------------------------------
+// ***** Linux DeviceManager
+
+class DeviceManager : public DeviceManagerImpl
+{
+public:
+ DeviceManager();
+ ~DeviceManager();
+
+ // Initialize/Shutdowncreate and shutdown manger thread.
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ virtual ThreadCommandQueue* GetThreadQueue();
+ virtual ThreadId GetThreadId() const;
+
+ virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args);
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+ Ptr<DeviceManagerThread> pThread;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** Device Manager Background Thread
+
+class DeviceManagerThread : public Thread, public ThreadCommandQueue
+{
+ friend class DeviceManager;
+ enum { ThreadStackSize = 64 * 1024 };
+public:
+ DeviceManagerThread();
+ ~DeviceManagerThread();
+
+ virtual int Run();
+
+ // ThreadCommandQueue notifications for CommandEvent handling.
+ virtual void OnPushNonEmpty_Locked() { write(CommandFd[1], this, 1); }
+ virtual void OnPopEmpty_Locked() { }
+
+ class Notifier
+ {
+ public:
+ // Called when I/O is received
+ virtual void OnEvent(int i, int fd) = 0;
+
+ // Called when timing ticks are updated.
+ // Returns the largest number of seconds this function can
+ // wait till next call.
+ virtual double OnTicks(double tickSeconds)
+ {
+ OVR_UNUSED1(tickSeconds);
+ return 1000.0;
+ }
+ };
+
+ // Add I/O notifier
+ bool AddSelectFd(Notifier* notify, int fd);
+ bool RemoveSelectFd(Notifier* notify, int fd);
+
+ // Add notifier that will be called at regular intervals.
+ bool AddTicksNotifier(Notifier* notify);
+ bool RemoveTicksNotifier(Notifier* notify);
+
+private:
+
+ bool threadInitialized() { return CommandFd[0] != 0; }
+
+ // pipe used to signal commands
+ int CommandFd[2];
+
+ Array<struct pollfd> PollFds;
+ Array<Notifier*> FdNotifiers;
+
+ Event StartupEvent;
+
+ // Ticks notifiers - used for time-dependent events such as keep-alive.
+ Array<Notifier*> TicksNotifiers;
+};
+
+}} // namespace Linux::OVR
+
+#endif // OVR_Linux_DeviceManager_h
diff --git a/LibOVR/Src/OVR_Linux_HIDDevice.cpp b/LibOVR/Src/OVR_Linux_HIDDevice.cpp
new file mode 100644
index 0000000..133e5c3
--- /dev/null
+++ b/LibOVR/Src/OVR_Linux_HIDDevice.cpp
@@ -0,0 +1,819 @@
+/************************************************************************************
+Filename : OVR_Linux_HIDDevice.cpp
+Content : Linux HID device implementation.
+Created : February 26, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#include "OVR_Linux_HIDDevice.h"
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <linux/hidraw.h>
+#include "OVR_HIDDeviceImpl.h"
+
+namespace OVR { namespace Linux {
+
+static const UInt32 MAX_QUEUED_INPUT_REPORTS = 5;
+
+//-------------------------------------------------------------------------------------
+// **** Linux::DeviceManager
+//-----------------------------------------------------------------------------
+HIDDeviceManager::HIDDeviceManager(DeviceManager* manager) : DevManager(manager)
+{
+ UdevInstance = NULL;
+ HIDMonitor = NULL;
+ HIDMonHandle = -1;
+}
+
+//-----------------------------------------------------------------------------
+HIDDeviceManager::~HIDDeviceManager()
+{
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::initializeManager()
+{
+ if (HIDMonitor)
+ {
+ return true;
+ }
+
+ // Create a udev_monitor handle to watch for device changes (hot-plug detection)
+ HIDMonitor = udev_monitor_new_from_netlink(UdevInstance, "udev");
+ if (HIDMonitor == NULL)
+ {
+ return false;
+ }
+
+ udev_monitor_filter_add_match_subsystem_devtype(HIDMonitor, "hidraw", NULL); // filter for hidraw only
+
+ int err = udev_monitor_enable_receiving(HIDMonitor);
+ if (err)
+ {
+ udev_monitor_unref(HIDMonitor);
+ HIDMonitor = NULL;
+ return false;
+ }
+
+ // Get the file descriptor (fd) for the monitor.
+ HIDMonHandle = udev_monitor_get_fd(HIDMonitor);
+ if (HIDMonHandle < 0)
+ {
+ udev_monitor_unref(HIDMonitor);
+ HIDMonitor = NULL;
+ return false;
+ }
+
+ // This file handle will be polled along-side with the device hid handles for changes
+ // Add the handle to the polling list
+ if (!DevManager->pThread->AddSelectFd(this, HIDMonHandle))
+ {
+ close(HIDMonHandle);
+ HIDMonHandle = -1;
+
+ udev_monitor_unref(HIDMonitor);
+ HIDMonitor = NULL;
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::Initialize()
+{
+ // Get a udev library handle. This handle must stay active during the
+ // duration the lifetime of device monitoring handles
+ UdevInstance = udev_new();
+ if (!UdevInstance)
+ return false;
+
+ return initializeManager();
+}
+
+//-----------------------------------------------------------------------------
+void HIDDeviceManager::Shutdown()
+{
+ OVR_ASSERT_LOG((UdevInstance), ("Should have called 'Initialize' before 'Shutdown'."));
+
+ if (HIDMonitor)
+ {
+ DevManager->pThread->RemoveSelectFd(this, HIDMonHandle);
+ close(HIDMonHandle);
+ HIDMonHandle = -1;
+
+ udev_monitor_unref(HIDMonitor);
+ HIDMonitor = NULL;
+ }
+
+ udev_unref(UdevInstance); // release the library
+
+ LogText("OVR::Linux::HIDDeviceManager - shutting down.\n");
+}
+
+//-------------------------------------------------------------------------------
+bool HIDDeviceManager::AddNotificationDevice(HIDDevice* device)
+{
+ NotificationDevices.PushBack(device);
+ return true;
+}
+
+//-------------------------------------------------------------------------------
+bool HIDDeviceManager::RemoveNotificationDevice(HIDDevice* device)
+{
+ for (UPInt i = 0; i < NotificationDevices.GetSize(); i++)
+ {
+ if (NotificationDevices[i] == device)
+ {
+ NotificationDevices.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::getIntProperty(udev_device* device,
+ const char* propertyName,
+ SInt32* pResult)
+{
+ const char* str = udev_device_get_sysattr_value(device, propertyName);
+ if (str)
+ {
+ *pResult = strtol(str, NULL, 16);
+ return true;
+ }
+ else
+ {
+ *pResult = 0;
+ return true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::initVendorProductVersion(udev_device* device, HIDDeviceDesc* pDevDesc)
+{
+ SInt32 result;
+ if (getIntProperty(device, "idVendor", &result))
+ pDevDesc->VendorId = result;
+ else
+ return false;
+
+ if (getIntProperty(device, "idProduct", &result))
+ pDevDesc->ProductId = result;
+ else
+ return false;
+
+ if (getIntProperty(device, "bcdDevice", &result))
+ pDevDesc->VersionNumber = result;
+ else
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::getStringProperty(udev_device* device,
+ const char* propertyName,
+ OVR::String* pResult)
+{
+ // Get the attribute in UTF8
+ const char* str = udev_device_get_sysattr_value(device, propertyName);
+ if (str)
+ { // Copy the string into the return value
+ *pResult = String(str);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor)
+{
+
+ if (!initializeManager())
+ {
+ return false;
+ }
+
+ // Get a list of hid devices
+ udev_enumerate* devices = udev_enumerate_new(UdevInstance);
+ udev_enumerate_add_match_subsystem(devices, "hidraw");
+ udev_enumerate_scan_devices(devices);
+
+ udev_list_entry* entry = udev_enumerate_get_list_entry(devices);
+
+ // Search each device for the matching vid/pid
+ while (entry != NULL)
+ {
+ // Get the device file name
+ const char* sysfs_path = udev_list_entry_get_name(entry);
+ udev_device* hid; // The device's HID udev node.
+ hid = udev_device_new_from_syspath(UdevInstance, sysfs_path);
+ const char* dev_path = udev_device_get_devnode(hid);
+
+ // Get the USB device
+ hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device");
+ if (hid)
+ {
+ HIDDeviceDesc devDesc;
+
+ // Check the VID/PID for a match
+ if (dev_path &&
+ initVendorProductVersion(hid, &devDesc) &&
+ enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId))
+ {
+ devDesc.Path = dev_path;
+ getFullDesc(hid, &devDesc);
+
+ // Look for the device to check if it is already opened.
+ Ptr<DeviceCreateDesc> existingDevice = DevManager->FindHIDDevice(devDesc, true);
+ // if device exists and it is opened then most likely the device open()
+ // will fail; therefore, we just set Enumerated to 'true' and continue.
+ if (existingDevice && existingDevice->pDevice)
+ {
+ existingDevice->Enumerated = true;
+ }
+ else
+ { // open the device temporarily for startup communication
+ int device_handle = open(dev_path, O_RDWR);
+ if (device_handle >= 0)
+ {
+ // Construct minimal device that the visitor callback can get feature reports from
+ Linux::HIDDevice device(this, device_handle);
+ enumVisitor->Visit(device, devDesc);
+
+ close(device_handle); // close the file handle
+ }
+ }
+ }
+
+ udev_device_unref(hid);
+ entry = udev_list_entry_get_next(entry);
+ }
+ }
+
+ // Free the enumerator and udev objects
+ udev_enumerate_unref(devices);
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+OVR::HIDDevice* HIDDeviceManager::Open(const String& path)
+{
+ Ptr<Linux::HIDDevice> device = *new Linux::HIDDevice(this);
+
+ if (device->HIDInitialize(path))
+ {
+ device->AddRef();
+ return device;
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::getFullDesc(udev_device* device, HIDDeviceDesc* desc)
+{
+
+ if (!initVendorProductVersion(device, desc))
+ {
+ return false;
+ }
+
+ if (!getStringProperty(device, "serial", &(desc->SerialNumber)))
+ {
+ return false;
+ }
+
+ getStringProperty(device, "manufacturer", &(desc->Manufacturer));
+ getStringProperty(device, "product", &(desc->Product));
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::GetDescriptorFromPath(const char* dev_path, HIDDeviceDesc* desc)
+{
+ if (!initializeManager())
+ {
+ return false;
+ }
+
+ // Search for the udev device from the given pathname so we can
+ // have a handle to query device properties
+
+ udev_enumerate* devices = udev_enumerate_new(UdevInstance);
+ udev_enumerate_add_match_subsystem(devices, "hidraw");
+ udev_enumerate_scan_devices(devices);
+
+ udev_list_entry* entry = udev_enumerate_get_list_entry(devices);
+
+ bool success = false;
+ // Search for the device with the matching path
+ while (entry != NULL)
+ {
+ // Get the device file name
+ const char* sysfs_path = udev_list_entry_get_name(entry);
+ udev_device* hid; // The device's HID udev node.
+ hid = udev_device_new_from_syspath(UdevInstance, sysfs_path);
+ const char* path = udev_device_get_devnode(hid);
+
+ if (OVR_strcmp(dev_path, path) == 0)
+ { // Found the device so lets collect the device descriptor
+
+ // Get the USB device
+ hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device");
+ if (hid)
+ {
+ desc->Path = dev_path;
+ success = getFullDesc(hid, desc);
+ }
+
+ }
+
+ udev_device_unref(hid);
+ entry = udev_list_entry_get_next(entry);
+ }
+
+ // Free the enumerator
+ udev_enumerate_unref(devices);
+
+ return success;
+}
+
+//-----------------------------------------------------------------------------
+void HIDDeviceManager::OnEvent(int i, int fd)
+{
+ OVR_UNUSED(i);
+ OVR_UNUSED(fd);
+
+ // There is a device status change
+ udev_device* hid = udev_monitor_receive_device(HIDMonitor);
+ if (hid)
+ {
+ const char* dev_path = udev_device_get_devnode(hid);
+ const char* action = udev_device_get_action(hid);
+
+ HIDDeviceDesc device_info;
+ device_info.Path = dev_path;
+
+ MessageType notify_type;
+ if (OVR_strcmp(action, "add") == 0)
+ {
+ notify_type = Message_DeviceAdded;
+
+ // Retrieve the device info. This can only be done on a connected
+ // device and is invalid for a disconnected device
+
+ // Get the USB device
+ hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device");
+ if (!hid)
+ {
+ return;
+ }
+
+ getFullDesc(hid, &device_info);
+ }
+ else if (OVR_strcmp(action, "remove") == 0)
+ {
+ notify_type = Message_DeviceRemoved;
+ }
+ else
+ {
+ return;
+ }
+
+ bool error = false;
+ bool deviceFound = false;
+ for (UPInt i = 0; i < NotificationDevices.GetSize(); i++)
+ {
+ if (NotificationDevices[i] &&
+ NotificationDevices[i]->OnDeviceNotification(notify_type, &device_info, &error))
+ {
+ // The notification was for an existing device
+ deviceFound = true;
+ break;
+ }
+ }
+
+ if (notify_type == Message_DeviceAdded && !deviceFound)
+ {
+ DevManager->DetectHIDDevice(device_info);
+ }
+
+ udev_device_unref(hid);
+ }
+}
+
+//=============================================================================
+// Linux::HIDDevice
+//=============================================================================
+HIDDevice::HIDDevice(HIDDeviceManager* manager)
+ : InMinimalMode(false), HIDManager(manager)
+{
+ DeviceHandle = -1;
+}
+
+//-----------------------------------------------------------------------------
+// This is a minimal constructor used during enumeration for us to pass
+// a HIDDevice to the visit function (so that it can query feature reports).
+HIDDevice::HIDDevice(HIDDeviceManager* manager, int device_handle)
+: InMinimalMode(true), HIDManager(manager), DeviceHandle(device_handle)
+{
+}
+
+//-----------------------------------------------------------------------------
+HIDDevice::~HIDDevice()
+{
+ if (!InMinimalMode)
+ {
+ HIDShutdown();
+ }
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDevice::HIDInitialize(const String& path)
+{
+ const char* hid_path = path.ToCStr();
+ if (!openDevice(hid_path))
+ {
+ LogText("OVR::Linux::HIDDevice - Failed to open HIDDevice: %s", hid_path);
+ return false;
+ }
+
+ HIDManager->DevManager->pThread->AddTicksNotifier(this);
+ HIDManager->AddNotificationDevice(this);
+
+ LogText("OVR::Linux::HIDDevice - Opened '%s'\n"
+ " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n",
+ DevDesc.Path.ToCStr(),
+ DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(),
+ DevDesc.SerialNumber.ToCStr());
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDevice::initInfo()
+{
+ // Device must have been successfully opened.
+ OVR_ASSERT(DeviceHandle >= 0);
+
+ int desc_size = 0;
+ hidraw_report_descriptor rpt_desc;
+ memset(&rpt_desc, 0, sizeof(rpt_desc));
+
+ // get report descriptor size
+ int r = ioctl(DeviceHandle, HIDIOCGRDESCSIZE, &desc_size);
+ if (r < 0)
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get report descriptor size."));
+ return false;
+ }
+
+ // Get the report descriptor
+ rpt_desc.size = desc_size;
+ r = ioctl(DeviceHandle, HIDIOCGRDESC, &rpt_desc);
+ if (r < 0)
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get report descriptor."));
+ return false;
+ }
+
+ /*
+ // Get report lengths.
+ SInt32 bufferLength;
+ bool getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxInputReportSizeKey), &bufferLength);
+ OVR_ASSERT(getResult);
+ InputReportBufferLength = (UInt16) bufferLength;
+
+ getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxOutputReportSizeKey), &bufferLength);
+ OVR_ASSERT(getResult);
+ OutputReportBufferLength = (UInt16) bufferLength;
+
+ getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxFeatureReportSizeKey), &bufferLength);
+ OVR_ASSERT(getResult);
+ FeatureReportBufferLength = (UInt16) bufferLength;
+
+
+ if (ReadBufferSize < InputReportBufferLength)
+ {
+ OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer."));
+ return false;
+ }
+
+ // Get device desc.
+ if (!HIDManager->getFullDesc(Device, &DevDesc))
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device."));
+ return false;
+ }
+
+ return true;
+ */
+
+ // Get report lengths.
+// TODO: hard-coded for now. Need to interpret these values from the report descriptor
+ InputReportBufferLength = 62;
+ OutputReportBufferLength = 0;
+ FeatureReportBufferLength = 69;
+
+ if (ReadBufferSize < InputReportBufferLength)
+ {
+ OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer."));
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDevice::openDevice(const char* device_path)
+{
+ // First fill out the device descriptor
+ if (!HIDManager->GetDescriptorFromPath(device_path, &DevDesc))
+ {
+ return false;
+ }
+
+ // Now open the device
+ DeviceHandle = open(device_path, O_RDWR);
+ if (DeviceHandle < 0)
+ {
+ OVR_DEBUG_LOG(("Failed 'CreateHIDFile' while opening device, error = 0x%X.", errno));
+ DeviceHandle = -1;
+ return false;
+ }
+
+ // fill out some values from the feature report descriptor
+ if (!initInfo())
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get HIDDevice info."));
+
+ close(DeviceHandle);
+ DeviceHandle = -1;
+ return false;
+ }
+
+ // Add the device to the polling list
+ if (!HIDManager->DevManager->pThread->AddSelectFd(this, DeviceHandle))
+ {
+ OVR_ASSERT_LOG(false, ("Failed to initialize polling for HIDDevice."));
+
+ close(DeviceHandle);
+ DeviceHandle = -1;
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+void HIDDevice::HIDShutdown()
+{
+
+ HIDManager->DevManager->pThread->RemoveTicksNotifier(this);
+ HIDManager->RemoveNotificationDevice(this);
+
+ if (DeviceHandle >= 0) // Device may already have been closed if unplugged.
+ {
+ closeDevice(false);
+ }
+
+ LogText("OVR::Linux::HIDDevice - HIDShutdown '%s'\n", DevDesc.Path.ToCStr());
+}
+
+//-----------------------------------------------------------------------------
+void HIDDevice::closeDevice(bool wasUnplugged)
+{
+ OVR_UNUSED(wasUnplugged);
+ OVR_ASSERT(DeviceHandle >= 0);
+
+
+ HIDManager->DevManager->pThread->RemoveSelectFd(this, DeviceHandle);
+
+ close(DeviceHandle); // close the file handle
+ DeviceHandle = -1;
+
+ LogText("OVR::Linux::HIDDevice - HID Device Closed '%s'\n", DevDesc.Path.ToCStr());
+}
+
+//-----------------------------------------------------------------------------
+void HIDDevice::closeDeviceOnIOError()
+{
+ LogText("OVR::Linux::HIDDevice - Lost connection to '%s'\n", DevDesc.Path.ToCStr());
+ closeDevice(false);
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length)
+{
+
+ if (DeviceHandle < 0)
+ return false;
+
+ UByte reportID = data[0];
+
+ if (reportID == 0)
+ {
+ // Not using reports so remove from data packet.
+ data++;
+ length--;
+ }
+
+ int r = ioctl(DeviceHandle, HIDIOCSFEATURE(length), data);
+ return (r >= 0);
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length)
+{
+ if (DeviceHandle < 0)
+ return false;
+
+ int r = ioctl(DeviceHandle, HIDIOCGFEATURE(length), data);
+ return (r >= 0);
+}
+
+//-----------------------------------------------------------------------------
+double HIDDevice::OnTicks(double tickSeconds)
+{
+ if (Handler)
+ {
+ return Handler->OnTicks(tickSeconds);
+ }
+
+ return DeviceManagerThread::Notifier::OnTicks(tickSeconds);
+}
+
+//-----------------------------------------------------------------------------
+void HIDDevice::OnEvent(int i, int fd)
+{
+ OVR_UNUSED(i);
+ // We have data to read from the device
+ int bytes = read(fd, ReadBuffer, ReadBufferSize);
+ if (bytes >= 0)
+ {
+// TODO: I need to handle partial messages and package reconstruction
+ if (Handler)
+ {
+ Handler->OnInputReport(ReadBuffer, bytes);
+ }
+ }
+ else
+ { // Close the device on read error.
+ closeDeviceOnIOError();
+ }
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDevice::OnDeviceNotification(MessageType messageType,
+ HIDDeviceDesc* device_info,
+ bool* error)
+{
+ const char* device_path = device_info->Path.ToCStr();
+
+ if (messageType == Message_DeviceAdded && DeviceHandle < 0)
+ {
+ // Is this the correct device?
+ if (!(device_info->VendorId == DevDesc.VendorId
+ && device_info->ProductId == DevDesc.ProductId
+ && device_info->SerialNumber == DevDesc.SerialNumber))
+ {
+ return false;
+ }
+
+ // A closed device has been re-added. Try to reopen.
+ if (!openDevice(device_path))
+ {
+ LogError("OVR::Linux::HIDDevice - Failed to reopen a device '%s' that was re-added.\n",
+ device_path);
+ *error = true;
+ return true;
+ }
+
+ LogText("OVR::Linux::HIDDevice - Reopened device '%s'\n", device_path);
+
+ if (Handler)
+ {
+ Handler->OnDeviceMessage(HIDHandler::HIDDeviceMessage_DeviceAdded);
+ }
+ }
+ else if (messageType == Message_DeviceRemoved)
+ {
+ // Is this the correct device?
+ // For disconnected device, the device description will be invalid so
+ // checking the path is the only way to match them
+ if (DevDesc.Path.CompareNoCase(device_path) != 0)
+ {
+ return false;
+ }
+
+ if (DeviceHandle >= 0)
+ {
+ closeDevice(true);
+ }
+
+ if (Handler)
+ {
+ Handler->OnDeviceMessage(HIDHandler::HIDDeviceMessage_DeviceRemoved);
+ }
+ }
+ else
+ {
+ OVR_ASSERT(0);
+ }
+
+ *error = false;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+HIDDeviceManager* HIDDeviceManager::CreateInternal(Linux::DeviceManager* devManager)
+{
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<Linux::HIDDeviceManager> manager = *new Linux::HIDDeviceManager(devManager);
+
+ if (manager)
+ {
+ if (manager->Initialize())
+ {
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+ }
+
+ return manager.GetPtr();
+}
+
+} // namespace Linux
+
+//-------------------------------------------------------------------------------------
+// ***** Creation
+
+// Creates a new HIDDeviceManager and initializes OVR.
+HIDDeviceManager* HIDDeviceManager::Create(Ptr<OVR::DeviceManager>& deviceManager)
+{
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<Linux::DeviceManager> deviceManagerLinux = *new Linux::DeviceManager;
+
+ if (!deviceManagerLinux)
+ {
+ return NULL;
+ }
+
+ if (!deviceManagerLinux->Initialize(NULL))
+ {
+ return NULL;
+ }
+
+ deviceManager = deviceManagerLinux;
+
+ return deviceManagerLinux->GetHIDDeviceManager();
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_Linux_HIDDevice.h b/LibOVR/Src/OVR_Linux_HIDDevice.h
new file mode 100644
index 0000000..52f2d69
--- /dev/null
+++ b/LibOVR/Src/OVR_Linux_HIDDevice.h
@@ -0,0 +1,135 @@
+/************************************************************************************
+Filename : OVR_Linux_HIDDevice.h
+Content : Linux HID device implementation.
+Created : June 13, 2013
+Authors : Brant Lewis
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#ifndef OVR_LINUX_HIDDevice_h
+#define OVR_LINUX_HIDDevice_h
+
+#include "OVR_HIDDevice.h"
+#include "OVR_Linux_DeviceManager.h"
+#include <libudev.h>
+
+namespace OVR { namespace Linux {
+
+class HIDDeviceManager;
+
+//-------------------------------------------------------------------------------------
+// ***** Linux HIDDevice
+
+class HIDDevice : public OVR::HIDDevice, public DeviceManagerThread::Notifier
+{
+private:
+ friend class HIDDeviceManager;
+
+public:
+ HIDDevice(HIDDeviceManager* manager);
+
+ // This is a minimal constructor used during enumeration for us to pass
+ // a HIDDevice to the visit function (so that it can query feature reports).
+ HIDDevice(HIDDeviceManager* manager, int device_handle);
+
+ virtual ~HIDDevice();
+
+ bool HIDInitialize(const String& path);
+ void HIDShutdown();
+
+ virtual bool SetFeatureReport(UByte* data, UInt32 length);
+ virtual bool GetFeatureReport(UByte* data, UInt32 length);
+
+ // DeviceManagerThread::Notifier
+ void OnEvent(int i, int fd);
+ double OnTicks(double tickSeconds);
+
+ bool OnDeviceNotification(MessageType messageType,
+ HIDDeviceDesc* device_info,
+ bool* error);
+
+private:
+ bool initInfo();
+ bool openDevice(const char* dev_path);
+ void closeDevice(bool wasUnplugged);
+ void closeDeviceOnIOError();
+ bool setupDevicePluggedInNotification();
+
+ bool InMinimalMode;
+ HIDDeviceManager* HIDManager;
+ int DeviceHandle; // file handle to the device
+ HIDDeviceDesc DevDesc;
+
+ enum { ReadBufferSize = 96 };
+ UByte ReadBuffer[ReadBufferSize];
+
+ UInt16 InputReportBufferLength;
+ UInt16 OutputReportBufferLength;
+ UInt16 FeatureReportBufferLength;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** Linux HIDDeviceManager
+
+class HIDDeviceManager : public OVR::HIDDeviceManager, public DeviceManagerThread::Notifier
+{
+ friend class HIDDevice;
+
+public:
+ HIDDeviceManager(Linux::DeviceManager* Manager);
+ virtual ~HIDDeviceManager();
+
+ virtual bool Initialize();
+ virtual void Shutdown();
+
+ virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor);
+ virtual OVR::HIDDevice* Open(const String& path);
+
+ static HIDDeviceManager* CreateInternal(DeviceManager* manager);
+
+ void OnEvent(int i, int fd);
+
+private:
+ bool initializeManager();
+ bool initVendorProductVersion(udev_device* device, HIDDeviceDesc* pDevDesc);
+ bool getPath(udev_device* device, String* pPath);
+ bool getIntProperty(udev_device* device, const char* key, int32_t* pResult);
+ bool getStringProperty(udev_device* device,
+ const char* propertyName,
+ OVR::String* pResult);
+ bool getFullDesc(udev_device* device, HIDDeviceDesc* desc);
+ bool GetDescriptorFromPath(const char* dev_path, HIDDeviceDesc* desc);
+
+ bool AddNotificationDevice(HIDDevice* device);
+ bool RemoveNotificationDevice(HIDDevice* device);
+
+ DeviceManager* DevManager;
+
+ udev* UdevInstance; // a handle to the udev library instance
+ udev_monitor* HIDMonitor;
+ int HIDMonHandle; // the udev_monitor file handle
+
+ Array<HIDDevice*> NotificationDevices;
+};
+
+}} // namespace OVR::Linux
+
+#endif // OVR_Linux_HIDDevice_h
diff --git a/LibOVR/Src/OVR_Linux_HMDDevice.cpp b/LibOVR/Src/OVR_Linux_HMDDevice.cpp
new file mode 100644
index 0000000..98143d3
--- /dev/null
+++ b/LibOVR/Src/OVR_Linux_HMDDevice.cpp
@@ -0,0 +1,291 @@
+/************************************************************************************
+
+Filename : OVR_Linux_HMDDevice.h
+Content : Linux HMDDevice implementation
+Created : June 17, 2013
+Authors : Brant Lewis
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#include "OVR_Linux_HMDDevice.h"
+
+#include "OVR_Linux_DeviceManager.h"
+
+#include "OVR_Profile.h"
+
+#include "../../3rdParty/EDID/edid.h"
+
+namespace OVR { namespace Linux {
+
+//-------------------------------------------------------------------------------------
+
+HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory, const String& displayDeviceName, long dispId)
+ : DeviceCreateDesc(factory, Device_HMD),
+ DisplayDeviceName(displayDeviceName),
+ Contents(0),
+ DisplayId(dispId)
+{
+ DeviceId = DisplayDeviceName;
+
+ Desktop.X = 0;
+ Desktop.Y = 0;
+ ResolutionInPixels = Sizei(0);
+ ScreenSizeInMeters = Sizef(0.0f);
+ VCenterFromTopInMeters = 0.0f;
+ LensSeparationInMeters = 0.0f;
+}
+
+HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other)
+ : DeviceCreateDesc(other.pFactory, Device_HMD),
+ DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName),
+ Contents(other.Contents),
+ DisplayId(other.DisplayId)
+{
+ Desktop.X = other.Desktop.X;
+ Desktop.Y = other.Desktop.Y;
+ ResolutionInPixels = other.ResolutionInPixels;
+ ScreenSizeInMeters = other.ScreenSizeInMeters;
+ VCenterFromTopInMeters = other.VCenterFromTopInMeters;
+ LensSeparationInMeters = other.LensSeparationInMeters;
+}
+
+HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc** pcandidate) const
+{
+ if ((other.Type != Device_HMD) || (other.pFactory != pFactory))
+ return Match_None;
+
+ // There are several reasons we can come in here:
+ // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc
+ // - Require exact device DeviceId/DeviceName match
+ // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc
+ // - This DeviceId is empty; becomes candidate
+ // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc
+ // - This other.DeviceId is empty; becomes candidate
+
+ const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other;
+
+ if ((DeviceId == s2.DeviceId) &&
+ (DisplayId == s2.DisplayId))
+ {
+ // Non-null DeviceId may match while size is different if screen size was overwritten
+ // by SensorDisplayInfo in prior iteration.
+ if (!DeviceId.IsEmpty() ||
+ (ScreenSizeInMeters == s2.ScreenSizeInMeters) )
+ {
+ *pcandidate = 0;
+ return Match_Found;
+ }
+ }
+
+
+ // DisplayInfo takes precedence, although we try to match it first.
+ if ((ResolutionInPixels == s2.ResolutionInPixels) &&
+ (ScreenSizeInMeters == s2.ScreenSizeInMeters))
+ {
+ if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty())
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+
+ *pcandidate = 0;
+ return Match_Found;
+ }
+
+ // SensorDisplayInfo may override resolution settings, so store as candidate.
+ if (s2.DeviceId.IsEmpty())
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+ // OTHER HMD Monitor desc may initialize DeviceName/Id
+ else if (DeviceId.IsEmpty())
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+
+ return Match_None;
+}
+
+
+bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other,
+ bool* newDeviceFlag)
+{
+ // This candidate was the the "best fit" to apply sensor DisplayInfo to.
+ OVR_ASSERT(other.Type == Device_HMD);
+
+ const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other;
+
+ // Force screen size on resolution from SensorDisplayInfo.
+ // We do this because USB detection is more reliable as compared to HDMI EDID,
+ // which may be corrupted by splitter reporting wrong monitor
+ if (s2.DeviceId.IsEmpty())
+ {
+ ScreenSizeInMeters = s2.ScreenSizeInMeters;
+ Contents |= Contents_Screen;
+
+ if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion)
+ {
+ memcpy(DistortionK, s2.DistortionK, sizeof(float)*4);
+ // TODO: DistortionEqn
+ Contents |= Contents_Distortion;
+ }
+ DeviceId = s2.DeviceId;
+ DisplayId = s2.DisplayId;
+ DisplayDeviceName = s2.DisplayDeviceName;
+ Desktop.X = s2.Desktop.X;
+ Desktop.Y = s2.Desktop.Y;
+ if (newDeviceFlag) *newDeviceFlag = true;
+ }
+ else if (DeviceId.IsEmpty())
+ {
+ // This branch is executed when 'fake' HMD descriptor is being replaced by
+ // the real one.
+ DeviceId = s2.DeviceId;
+ DisplayId = s2.DisplayId;
+ DisplayDeviceName = s2.DisplayDeviceName;
+ Desktop.X = s2.Desktop.X;
+ Desktop.Y = s2.Desktop.Y;
+
+ // ScreenSize and Resolution are NOT assigned here, since they may have
+ // come from a sensor DisplayInfo (which has precedence over HDMI).
+
+ if (newDeviceFlag) *newDeviceFlag = true;
+ }
+ else
+ {
+ if (newDeviceFlag) *newDeviceFlag = false;
+ }
+
+ return true;
+}
+
+bool HMDDeviceCreateDesc::MatchDevice(const String& path)
+{
+ return DeviceId.CompareNoCase(path) == 0;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** HMDDeviceFactory
+
+HMDDeviceFactory &HMDDeviceFactory::GetInstance()
+{
+ static HMDDeviceFactory instance;
+ return instance;
+}
+
+void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor)
+{
+ // For now we'll assume the Rift DK1 is attached in extended monitor mode. Ultimately we need to
+ // use XFree86 to enumerate X11 screens in case the Rift is attached as a separate screen.
+
+ bool foundHMD = false;
+ Display* display = XOpenDisplay(NULL);
+ XRRScreenResources *screen = XRRGetScreenResources(display, DefaultRootWindow(display));
+ for (int iscres = screen->noutput - 1; iscres >= 0; --iscres) {
+ RROutput output = screen->outputs[iscres];
+ MonitorInfo * mi = read_edid_data(display, output);
+ if (mi == NULL) {
+ continue;
+ }
+
+ XRROutputInfo * info = XRRGetOutputInfo (display, screen, output);
+ if (info && (0 == memcmp(mi->manufacturer_code, "OVR", 3))) {
+
+ // Generate a device ID string similar to the way Windows does it
+ char device_id[32];
+ OVR_sprintf(device_id, 32, "%s%04d", mi->manufacturer_code, mi->product_code);
+
+ // The default monitor coordinates
+ int mx = 0;
+ int my = 0;
+ int mwidth = 1280;
+ int mheight = 800;
+
+ if (info->connection == RR_Connected && info->crtc) {
+ XRRCrtcInfo * crtc_info = XRRGetCrtcInfo (display, screen, info->crtc);
+ if (crtc_info)
+ {
+ mx = crtc_info->x;
+ my = crtc_info->y;
+ //mwidth = crtc_info->width;
+ //mheight = crtc_info->height;
+ XRRFreeCrtcInfo(crtc_info);
+ }
+ }
+
+ String deviceID = device_id;
+ HMDDeviceCreateDesc hmdCreateDesc(this, deviceID, iscres);
+
+ // Hard-coded defaults in case the device doesn't have the data itself.
+ if (strstr(device_id, "OVR0003"))
+ { // DK2 prototypes and variants (default to HmdType_DK2)
+ hmdCreateDesc.SetScreenParameters(mx, my, 1920, 1080, 0.12576f, 0.07074f, 0.12576f*0.5f, 0.0635f );
+ }
+ else if (strstr(device_id, "OVR0002"))
+ { // HD Prototypes (default to HmdType_DKHDProto)
+ hmdCreateDesc.SetScreenParameters(mx, my, 1920, 1080, 0.12096f, 0.06804f, 0.06804f*0.5f, 0.0635f );
+ }
+ else if (strstr(device_id, "OVR0001"))
+ { // DK1
+ hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.14976f, 0.0936f, 0.0936f*0.5f, 0.0635f);
+ }
+ else if (strstr(device_id, "OVR00"))
+ { // Future Oculus HMD devices (default to DK1 dimensions)
+ hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.14976f, 0.0936f, 0.0936f*0.5f, 0.0635f);
+ }
+ else
+ { // Duct-tape prototype
+ hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.12096f, 0.0756f, 0.0756f*0.5f, 0.0635f);
+ }
+
+ OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %s - %s\n", device_id, mi->dsc_product_name));
+
+ // Notify caller about detected device. This will call EnumerateAddDevice
+ // if the this is the first time device was detected.
+ visitor.Visit(hmdCreateDesc);
+ foundHMD = true;
+ break;
+ } // if
+
+ XRRFreeOutputInfo(info);
+ delete mi;
+ } // for
+ XRRFreeScreenResources(screen);
+
+
+ // Real HMD device is not found; however, we still may have a 'fake' HMD
+ // device created via SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo.
+ // Need to find it and set 'Enumerated' to true to avoid Removal notification.
+ if (!foundHMD)
+ {
+ Ptr<DeviceCreateDesc> hmdDevDesc = getManager()->FindDevice("", Device_HMD);
+ if (hmdDevDesc)
+ hmdDevDesc->Enumerated = true;
+ }
+}
+
+#include "OVR_Common_HMDDevice.cpp"
+
+}} // namespace OVR::Linux
+
+
diff --git a/LibOVR/Src/OVR_Linux_HMDDevice.h b/LibOVR/Src/OVR_Linux_HMDDevice.h
new file mode 100644
index 0000000..a8c044f
--- /dev/null
+++ b/LibOVR/Src/OVR_Linux_HMDDevice.h
@@ -0,0 +1,154 @@
+/************************************************************************************
+
+Filename : OVR_Linux_HMDDevice.h
+Content : Linux HMDDevice implementation
+Created : June 17, 2013
+Authors : Brant Lewis
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#ifndef OVR_Linux_HMDDevice_h
+#define OVR_Linux_HMDDevice_h
+
+#include "OVR_Linux_DeviceManager.h"
+#include "OVR_Profile.h"
+
+namespace OVR { namespace Linux {
+
+class HMDDevice;
+
+//-------------------------------------------------------------------------------------
+
+// HMDDeviceFactory enumerates attached Oculus HMD devices.
+//
+// This is currently done by matching monitor device strings.
+
+class HMDDeviceFactory : public DeviceFactory
+{
+public:
+ static HMDDeviceFactory &GetInstance();
+
+ // Enumerates devices, creating and destroying relevant objects in manager.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor);
+
+protected:
+ DeviceManager* getManager() const { return (DeviceManager*) pManager; }
+};
+
+
+class HMDDeviceCreateDesc : public DeviceCreateDesc
+{
+ friend class HMDDevice;
+
+protected:
+ enum
+ {
+ Contents_Screen = 1,
+ Contents_Distortion = 2,
+ };
+ String DeviceId;
+ String DisplayDeviceName;
+ struct
+ {
+ int X, Y;
+ } Desktop;
+ unsigned int Contents;
+
+ Sizei ResolutionInPixels;
+ Sizef ScreenSizeInMeters;
+ float VCenterFromTopInMeters;
+ float LensSeparationInMeters;
+
+ // TODO: update these to splines.
+ DistortionEqnType DistortionEqn;
+ float DistortionK[4];
+
+ long DisplayId;
+
+public:
+ HMDDeviceCreateDesc(DeviceFactory* factory,
+ const String& displayDeviceName, long dispId);
+ HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other);
+
+ virtual DeviceCreateDesc* Clone() const
+ {
+ return new HMDDeviceCreateDesc(*this);
+ }
+
+ virtual DeviceBase* NewDeviceInstance();
+
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc**) const;
+
+ // Matches device by path.
+ virtual bool MatchDevice(const String& path);
+
+ virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL);
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+ void SetScreenParameters(int x, int y,
+ int hres, int vres,
+ float hsize, float vsize,
+ float vCenterFromTopInMeters, float lensSeparationInMeters);
+ void SetDistortion(const float* dks);
+
+ HmdTypeEnum GetHmdType() const;
+};
+
+
+//-------------------------------------------------------------------------------------
+
+// HMDDevice represents an Oculus HMD device unit. An instance of this class
+// is typically created from the DeviceManager.
+// After HMD device is created, we its sensor data can be obtained by
+// first creating a Sensor object and then wrappig it in SensorFusion.
+
+class HMDDevice : public DeviceImpl<OVR::HMDDevice>
+{
+public:
+ HMDDevice(HMDDeviceCreateDesc* createDesc);
+ ~HMDDevice();
+
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ // Requests the currently used default profile. This profile affects the
+ // settings reported by HMDInfo.
+ virtual Profile* GetProfile();
+ virtual const char* GetProfileName();
+ virtual bool SetProfileName(const char* name);
+
+ // Query associated sensor.
+ virtual OVR::SensorDevice* GetSensor();
+
+protected:
+ HMDDeviceCreateDesc* getDesc() const { return (HMDDeviceCreateDesc*)pCreateDesc.GetPtr(); }
+
+ // User name for the profile used with this device.
+ String ProfileName;
+ mutable Ptr<Profile> pCachedProfile;
+};
+
+
+}} // namespace OVR::Linux
+
+#endif // OVR_Linux_HMDDevice_h
+
diff --git a/LibOVR/Src/OVR_Linux_SensorDevice.cpp b/LibOVR/Src/OVR_Linux_SensorDevice.cpp
new file mode 100644
index 0000000..5b671a6
--- /dev/null
+++ b/LibOVR/Src/OVR_Linux_SensorDevice.cpp
@@ -0,0 +1,57 @@
+/************************************************************************************
+
+Filename : OVR_Linux_SensorDevice.cpp
+Content : Linux SensorDevice implementation
+Created : June 13, 2013
+Authors : Brant Lewis
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#include "OVR_Linux_HMDDevice.h"
+#include "OVR_SensorImpl.h"
+#include "OVR_DeviceImpl.h"
+
+namespace OVR { namespace Linux {
+
+} // namespace Linux
+
+//-------------------------------------------------------------------------------------
+void SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo( const SensorDisplayInfoImpl& displayInfo,
+ DeviceFactory::EnumerateVisitor& visitor)
+{
+ Linux::HMDDeviceCreateDesc hmdCreateDesc(&Linux::HMDDeviceFactory::GetInstance(), String(), 0);
+
+ hmdCreateDesc.SetScreenParameters( 0, 0,
+ displayInfo.HResolution, displayInfo.VResolution,
+ displayInfo.HScreenSize, displayInfo.VScreenSize,
+ displayInfo.VCenter, displayInfo.LensSeparation);
+
+ if ((displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) == SensorDisplayInfoImpl::Base_Distortion)
+ {
+ // TODO: update to spline system.
+ hmdCreateDesc.SetDistortion(displayInfo.DistortionK);
+ }
+
+ visitor.Visit(hmdCreateDesc);
+}
+
+} // namespace OVR
+
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..62a3687
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,82 @@
+#############################################################################
+#
+# Filename : Makefile
+# Content : Makefile for building linux libovr and OculusWorldDemo
+# Created : 2013
+# Authors : Simon Hallam and Peter Giokaris
+# Copyright : Copyright 2013 OculusVR, Inc. All Rights Reserved
+# Instruction : The g++ compiler and stdndard lib packages need to be
+# installed on the system. Navigate in a shell to the
+# directory where this Makefile is located and enter:
+#
+# make builds the release versions for the
+# current architechture
+# make clean delete intermediate release object files
+# and library and executable
+# make DEBUG=1 builds the debug version for the current
+# architechture
+# make clean DEBUG=1 deletes intermediate debug object files
+# and the library and executable
+#
+# Output : Relative to the directory this Makefile lives in, libraries
+# and executables are built at the following locations
+# depending upon the architechture of the system you are
+# running:
+#
+# ./LibOVR/Lib/Linux/Debug/i386/libovr.a
+# ./LibOVR/Lib/Linux/Debug/x86_64/libovr.a
+# ./LibOVR/Lib/Linux/Release/i386/libovr.a
+# ./LibOVR/Lib/Linux/Release/x86_64/libovr.a
+# ./Samples/OculusWorldDemo/Release/OculusWorldDemo_i386_Release
+# ./Samples/OculusWorldDemo/Release/OculusWorldDemo_x86_64_Release
+# ./Samples/OculusWorldDemo/Release/OculusWorldDemo_i386_Debug
+# ./Samples/OculusWorldDemo/Release/OculusWorldDemo_x86_64_Debug
+#
+#############################################################################
+
+####### Detect system architecture
+
+SYSARCH = i386
+ifeq ($(shell uname -m),x86_64)
+SYSARCH = x86_64
+endif
+
+####### Compiler, tools and options
+
+CXX = g++
+LINK = ar rvs
+DELETEFILE = rm -f
+
+####### Detect debug or release
+
+DEBUG = 0
+ifeq ($(DEBUG), 1)
+ RELEASETYPE = Debug
+else
+ RELEASETYPE = Release
+endif
+
+####### Paths
+
+LIBOVRPATH = ./LibOVR
+DEMOPATH = ./Samples/OculusWorldDemo
+
+####### Files
+
+LIBOVRTARGET = $(LIBOVRPATH)/Lib/Linux/$(RELEASETYPE)/$(SYSARCH)/libovr.a
+DEMOTARGET = $(DEMOPATH)/Release/OculusWorldDemo_$(RELEASETYPE)/$(SYSARCH)
+
+####### Rules
+
+all: $(LIBOVRTARGET) $(DEMOTARGET)
+
+$(DEMOTARGET): $(DEMOPATH)/Makefile
+ $(MAKE) -C $(DEMOPATH) DEBUG=$(DEBUG)
+
+$(LIBOVRTARGET): $(LIBOVRPATH)/Makefile
+ $(MAKE) -C $(LIBOVRPATH) DEBUG=$(DEBUG)
+
+clean:
+ $(MAKE) -C $(LIBOVRPATH) clean DEBUG=$(DEBUG)
+ $(MAKE) -C $(DEMOPATH) clean DEBUG=$(DEBUG)
+
diff --git a/OculusConfigurationUtility.sh b/OculusConfigurationUtility.sh
new file mode 100644
index 0000000..a70d7f6
--- /dev/null
+++ b/OculusConfigurationUtility.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+#############################################################################
+#
+# Filename : OculusConfigurationUtility.sh
+# Content : Linux file for starting the OculusConfigurationUtility app
+# from the SDK package root. It will determine the
+# architechture of the system it is running on, and start the
+# approprite executable
+# Created : 2013
+# Authors : Simon Hallam
+# Copyright : Copyright 2013 OculusVR, Inc. All Rights Reserved
+# Instruction : Ensure that the OculusConfigurationUtility.sh has execute
+# permissions. Navigate to a command shell, enter:
+#
+# ./OculusConfigurationUtility.sh
+#
+#############################################################################
+
+MACHINE_TYPE=`uname -m`
+if [ ${MACHINE_TYPE} == 'x86_64' ]; then
+ ./Tools/OculusConfigUtil/OculusConfigUtil_x86_64
+elif [ ${MACHINE_TYPE} == 'i686' ]; then
+ ./Tools/OculusConfigUtil/OculusConfigUtil_i386
+else
+ echo "The Oculus Configuration Utility does not currently support this platform."
+fi
+
diff --git a/OculusWorldDemo.sh b/OculusWorldDemo.sh
new file mode 100644
index 0000000..e668c93
--- /dev/null
+++ b/OculusWorldDemo.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+#############################################################################
+#
+# Filename : OculusWorldDemo.sh
+# Content : Linux file for starting the OculusWorldDemo app from the SDK
+# package root. It will determine the architechture of the
+# system it is running on, and start the approprite executable
+# Created : 2013
+# Authors : Simon Hallam
+# Copyright : Copyright 2013 OculusVR, Inc. All Rights Reserved
+# Instruction : Ensure that the OculusWorldDemo.sh has execute permissions.
+# Navigate to a command shell, enter:
+#
+# ./OculusWorldDemo.sh
+#
+#############################################################################
+
+MACHINE_TYPE=`uname -m`
+if [ ${MACHINE_TYPE} == 'x86_64' ]; then
+ ./Samples/OculusWorldDemo/Release/OculusWorldDemo_x86_64_Release
+else
+ ./Samples/OculusWorldDemo/Release/OculusWorldDemo_i386_Release
+fi
+
diff --git a/Samples/CommonSrc/Platform/Linux_Gamepad.cpp b/Samples/CommonSrc/Platform/Linux_Gamepad.cpp
new file mode 100644
index 0000000..1546768
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Linux_Gamepad.cpp
@@ -0,0 +1,453 @@
+/************************************************************************************
+
+Filename : Linux_Gamepad.cpp
+Content : Linux implementation of Platform app infrastructure
+Created : May 6, 2013
+Authors : Lee Cooper, Simon Hallam
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <glob.h>
+#include <linux/joystick.h>
+#include "Linux_Gamepad.h"
+
+
+namespace OVR { namespace Platform { namespace Linux {
+
+const char* pNameXbox360Wireless = "Xbox 360";
+const char* pNameXbox360Wired = "Microsoft X-Box 360";
+
+
+GamepadManager::GamepadManager() :
+ pDevice(NULL)
+{
+}
+
+GamepadManager::~GamepadManager()
+{
+ // if we have an open device, close it
+ if (pDevice)
+ {
+ pDevice->Close();
+ pDevice = NULL;
+ }
+}
+
+UInt32 GamepadManager::GetGamepadCount()
+{
+ return 1;
+}
+
+bool GamepadManager::GetGamepadState(UInt32 index, GamepadState *pState)
+{
+ if (!pDevice)
+ {
+ // get a list of paths to all the connected joystick devices
+ glob_t joystickGlobBuffer;
+ glob("/dev/input/js*", 0, NULL, &joystickGlobBuffer);
+
+ // open each joystick device, until we find one that will work for our needs
+ for (UInt32 i = 0; i < joystickGlobBuffer.gl_pathc; i++)
+ {
+ pDevice = new Gamepad();
+ if (pDevice->Open(joystickGlobBuffer.gl_pathv[i]))
+ {
+
+ if (pDevice->IsSupportedType())
+ {
+ break;
+ }
+ }
+
+ // we don't know why the device was not useable, make sure it gets closed cleanly
+ pDevice->Close();
+ pDevice = NULL;
+ }
+
+ }
+
+ if (pDevice)
+ {
+ // we have a device, so update it
+ pDevice->UpdateState();
+
+ // copy the device state into the struct param
+ memcpy(pState, pDevice->GetState(), sizeof(GamepadState));
+
+ // TODO: is the device still active/connected? if not, we should close it
+ // and clear pDevice, so that another device can take over
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+Gamepad::Gamepad() :
+ IsInitialized(false),
+ Name(String("Undefined")),
+ Type(UNDEFINED)
+{
+}
+
+Gamepad::~Gamepad()
+{
+ this->Close();
+}
+
+bool Gamepad::Open(const String& devicePathName)
+{
+ Name = "Undefined";
+ Type = UNDEFINED;
+
+ FileDescriptor = ::open(devicePathName.ToCStr(), O_RDONLY | O_NONBLOCK);
+ if (FileDescriptor == -1)
+ {
+ return false;
+ }
+
+ // get the device name
+ char name[128];
+ if (ioctl(FileDescriptor, JSIOCGNAME(sizeof(name)), name) < 0)
+ {
+ return false;
+ }
+
+ Name = name;
+
+ // see if device name matches one of our supported devices
+ static const UInt32 Wireless360Len = String(pNameXbox360Wireless).GetLength();
+ static const UInt32 Wired360Len = String(pNameXbox360Wired).GetLength();
+ if (Name.Substring(0, Wireless360Len) == pNameXbox360Wireless)
+ {
+ Type = XBOX360GAMEPADWIRELESS;
+ return true;
+ }
+ else if(Name.Substring(0, Wired360Len) == pNameXbox360Wired)
+ {
+ Type = XBOX360GAMEPADWIRED;
+ return true;
+ }
+
+ return false;
+}
+
+bool Gamepad::Close()
+{
+ IsInitialized = false;
+ Name = "Undefined";
+ Type = UNDEFINED;
+ return !::close(FileDescriptor);
+}
+
+void Gamepad::UpdateState()
+{
+ GamepadState *pState = &State;
+ js_event gamepadEvent;
+
+ // read the latest batch of events
+ while (read(FileDescriptor, &gamepadEvent, sizeof(struct js_event)) != -1)
+ {
+ switch (gamepadEvent.type)
+ {
+ case JS_EVENT_BUTTON:
+ IsInitialized = true;
+ SetStateButton(pState, gamepadEvent.number, gamepadEvent.value);
+ break;
+
+ case JS_EVENT_AXIS:
+ IsInitialized = true;
+ SetStateAxis(pState, gamepadEvent.number, gamepadEvent.value);
+ break;
+
+ case JS_EVENT_BUTTON | JS_EVENT_INIT:
+ if (IsInitialized) // skip the fake values during device event initialization
+ {
+ SetStateButton(pState, gamepadEvent.number, gamepadEvent.value);
+ }
+ break;
+
+ case JS_EVENT_AXIS | JS_EVENT_INIT:
+ if (IsInitialized) // skip the fake values during device event initialization
+ {
+ SetStateAxis(pState, gamepadEvent.number, gamepadEvent.value);
+ }
+ break;
+
+ default:
+ LogText("OVR::Linux::UpdateState unknown event type\n");
+ }
+ }
+}
+
+const GamepadState* Gamepad::GetState()
+{
+ return &State;
+}
+
+
+bool Gamepad::IsSupportedType()
+{
+ return Type != UNDEFINED;
+}
+
+const String& Gamepad::GetIdentifier()
+{
+ return Name;
+}
+
+static inline float NormalizeGamepadStickXbox360(SInt32 in)
+{
+ float v;
+ if (abs(in) < 9000) return 0;
+ else if (in > 9000) v = (float)in - 9000;
+ else v = (float)in + 9000;
+ return v / (32767 - 9000);
+}
+
+static inline float NormalizeGamepadTriggerXbox360(SInt32 in,
+ SInt32 offset,
+ SInt32 deadBand,
+ float divisor)
+{
+ in += offset;
+
+ if (in < deadBand)
+ {
+ return 0;
+ }
+ else
+ {
+ return float(in - deadBand) / divisor;
+ }
+}
+
+static inline void UpdateButtonMaskAndBitfield(GamepadState *pState,
+ SInt32 value,
+ UInt32 buttonBitfield)
+{
+ if (value)
+ {
+ pState->Buttons |= buttonBitfield;
+ }
+ else
+ {
+ pState->Buttons = pState->Buttons & (0xFFFFFFFF ^ buttonBitfield);
+ }
+}
+
+void Gamepad::SetStateAxis(GamepadState *pState, UInt32 axis, SInt32 value)
+{
+ // some pads/sticks have lots in common with one another,
+ // handle those shared cases first
+ switch (Type)
+ {
+ case XBOX360GAMEPADWIRELESS:
+ case XBOX360GAMEPADWIRED:
+ switch (axis)
+ {
+ case 0:
+ pState->LX = NormalizeGamepadStickXbox360(value);
+ break;
+
+ case 1:
+ pState->LY = -NormalizeGamepadStickXbox360(value);
+ break;
+
+ case 3:
+ pState->RX = NormalizeGamepadStickXbox360(value);
+ break;
+
+ case 4:
+ pState->RY = -NormalizeGamepadStickXbox360(value);
+ break;
+ }
+ break;
+
+ case UNDEFINED:
+ default:
+ break;
+ }
+
+ // handle the special cases, or pads/sticks which are unique
+ switch (Type)
+ {
+ case XBOX360GAMEPADWIRELESS:
+ switch (axis)
+ {
+ case 2:
+ pState->LT = NormalizeGamepadTriggerXbox360(value, 0, 500, 32267);
+ break;
+
+ case 5:
+ pState->RT = NormalizeGamepadTriggerXbox360(value, 0, 500, 32267);
+ break;
+ }
+ break;
+
+ case XBOX360GAMEPADWIRED:
+ switch (axis)
+ {
+ case 2:
+ pState->LT = NormalizeGamepadTriggerXbox360(value, 32767, 1000, 64535);
+ break;
+
+ case 5:
+ pState->RT = NormalizeGamepadTriggerXbox360(value, 32767, 1000, 64535);
+ break;
+
+ case 6:
+ if (value == 0)
+ {
+ UpdateButtonMaskAndBitfield(pState, 0, Gamepad_Left);
+ UpdateButtonMaskAndBitfield(pState, 0, Gamepad_Right);
+ }
+ else if (value < 0)
+ {
+ UpdateButtonMaskAndBitfield(pState, 1, Gamepad_Left);
+ }
+ else if (value > 0)
+ {
+ UpdateButtonMaskAndBitfield(pState, 1, Gamepad_Right);
+ }
+ break;
+
+ case 7:
+ if (value == 0)
+ {
+ UpdateButtonMaskAndBitfield(pState, 0, Gamepad_Up);
+ UpdateButtonMaskAndBitfield(pState, 0, Gamepad_Down);
+ }
+ else if (value < 0)
+ {
+ UpdateButtonMaskAndBitfield(pState, 1, Gamepad_Up);
+ }
+ else if (value > 0)
+ {
+ UpdateButtonMaskAndBitfield(pState, 1, Gamepad_Down);
+ }
+ break;
+ }
+ break;
+
+ case UNDEFINED:
+ default:
+ break;
+ }
+}
+
+void Gamepad::SetStateButton(GamepadState *pState, UInt32 button, SInt32 value)
+{
+ // some pads/sticks have lots in common with one another,
+ // handle those shared cases first
+ switch (Type)
+ {
+ case XBOX360GAMEPADWIRELESS:
+ case XBOX360GAMEPADWIRED:
+ switch (button)
+ {
+ case 0:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_A);
+ break;
+
+ case 1:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_B);
+ break;
+
+ case 2:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_X);
+ break;
+
+ case 3:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_Y);
+ break;
+
+ case 4:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_L1);
+ break;
+
+ case 5:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_R1);
+ break;
+
+ case 6:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_Back);
+ break;
+
+ case 7:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_Start);
+ break;
+
+ case 8:
+ // we have no value defined for the Xbox/power button
+ break;
+
+ case 9:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_LStick);
+ break;
+
+ case 10:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_RStick);
+ break;
+ }
+ break;
+
+ case UNDEFINED:
+ default:
+ break;
+ }
+
+ // handle the special cases, or pads/sticks which are unique
+ switch (Type)
+ {
+ case XBOX360GAMEPADWIRELESS:
+ switch (button)
+ {
+ case 11:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_Left);
+ break;
+
+ case 12:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_Right);
+ break;
+
+ case 13:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_Up);
+ break;
+
+ case 14:
+ UpdateButtonMaskAndBitfield(pState, value, Gamepad_Down);
+ break;
+ }
+
+ case XBOX360GAMEPADWIRED:
+ break;
+
+ case UNDEFINED:
+ default:
+ break;
+ }
+}
+
+}}} // OVR::Platform::Linux
+
diff --git a/Samples/CommonSrc/Platform/Linux_Gamepad.h b/Samples/CommonSrc/Platform/Linux_Gamepad.h
new file mode 100644
index 0000000..762d7a9
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Linux_Gamepad.h
@@ -0,0 +1,83 @@
+/************************************************************************************
+
+Filename : Linux_Gamepad.h
+Content : Linux implementation of Gamepad functionality.
+Created : May 6, 2013
+Authors : Lee Cooper, Simon Hallam
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Linux_Gamepad_h
+#define OVR_Linux_Gamepad_h
+
+#include "Gamepad.h"
+
+namespace OVR { namespace Platform { namespace Linux {
+
+class Gamepad; // forward declaration for readability
+
+class GamepadManager : public Platform::GamepadManager
+{
+public:
+
+ GamepadManager();
+ ~GamepadManager();
+
+ virtual UInt32 GetGamepadCount();
+ virtual bool GetGamepadState(UInt32 index, GamepadState *pState);
+
+private:
+
+ Gamepad *pDevice;
+};
+
+class Gamepad
+{
+public:
+
+ Gamepad();
+ virtual ~Gamepad();
+
+ bool Open(const String& devicePathName);
+ bool Close();
+ bool IsSupportedType();
+ const String& GetIdentifier();
+ void UpdateState();
+ const GamepadState* GetState();
+
+private:
+
+ void SetStateAxis(GamepadState *pState, UInt32 axis, SInt32 value);
+ void SetStateButton(GamepadState *pState, UInt32 button, SInt32 value);
+
+ enum GamepadType
+ {
+ UNDEFINED,
+ XBOX360GAMEPADWIRELESS,
+ XBOX360GAMEPADWIRED
+ };
+
+ UInt32 FileDescriptor;
+ bool IsInitialized;
+ String Name;
+ GamepadType Type;
+ GamepadState State;
+};
+
+}}}
+
+#endif // OVR_Linux_Gamepad_h
diff --git a/Samples/CommonSrc/Platform/Linux_Platform.cpp b/Samples/CommonSrc/Platform/Linux_Platform.cpp
new file mode 100644
index 0000000..bf84270
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Linux_Platform.cpp
@@ -0,0 +1,784 @@
+/************************************************************************************
+
+Filename : Platform_Linux.cpp
+Content : Linux (X11) implementation of Platform app infrastructure
+Created : September 6, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "Kernel/OVR_System.h"
+#include "Kernel/OVR_Array.h"
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_Timer.h"
+#include "OVR_CAPI_GL.h"
+
+#include "Linux_Platform.h"
+#include "Linux_Gamepad.h"
+
+// Renderers
+#include "../Render/Render_GL_Device.h"
+
+#include "../../3rdParty/EDID/edid.h"
+#include <X11/extensions/Xinerama.h>
+#include <X11/extensions/Xrandr.h>
+
+
+namespace OVR { namespace Platform { namespace Linux {
+
+static const char *AtomNames[] = {"WM_PROTOCOLS", "WM_DELETE_WINDOW"};
+
+PlatformCore::PlatformCore(Application* app)
+ : Platform::PlatformCore(app), Disp(NULL), Win(0), Vis(NULL), Quit(0), MMode(Mouse_Normal)
+ , StartVP(0, 0, 0, 0)
+{
+ pGamepadManager = *new Linux::GamepadManager();
+}
+PlatformCore::~PlatformCore()
+{
+ XFreeCursor(Disp, InvisibleCursor);
+
+ if (Disp)
+ XCloseDisplay(Disp);
+}
+
+// Setup an X11 window in windowed mode.
+bool PlatformCore::SetupWindow(int w, int h)
+{
+
+ if (!Disp)
+ {
+ XInitThreads();
+
+ Disp = XOpenDisplay(NULL);
+ if (!Disp)
+ {
+ OVR_DEBUG_LOG(("XOpenDisplay failed."));
+ return false;
+ }
+
+ XInternAtoms(Disp, const_cast<char**>(AtomNames), NumAtoms, false, Atoms);
+ }
+
+ XSetWindowAttributes winattr;
+ unsigned attrmask = CWEventMask | CWBorderPixel;
+
+ winattr.event_mask = ButtonPressMask|ButtonReleaseMask|KeyPressMask|KeyReleaseMask|ButtonMotionMask|PointerMotionMask|
+ /*PointerMotionHintMask|*/StructureNotifyMask;//|ExposureMask;
+ winattr.border_pixel = 0;
+
+ int screenNumber = DefaultScreen(Disp);
+
+ if (!Vis)
+ {
+ int attr[16];
+ int nattr = 2;
+
+ attr[0] = GLX_RGBA;
+ attr[1] = GLX_DOUBLEBUFFER;
+ attr[nattr++] = GLX_DEPTH_SIZE;
+ attr[nattr++] = 24;
+ attr[nattr] = 0;
+
+ Vis = glXChooseVisual(Disp, screenNumber, attr);
+
+ if (!Vis)
+ {
+ OVR_DEBUG_LOG(("glXChooseVisual failed."));
+ return false;
+ }
+ }
+
+ Window rootWindow = XRootWindow(Disp, Vis->screen);
+
+ winattr.colormap = XCreateColormap(Disp, rootWindow, Vis->visual, AllocNone);
+ attrmask |= CWColormap;
+
+
+ Win = XCreateWindow(Disp, rootWindow, 0, 0, w, h, 0, Vis->depth,
+ InputOutput, Vis->visual, attrmask, &winattr);
+
+ if (!Win)
+ {
+ OVR_DEBUG_LOG(("XCreateWindow failed."));
+ return false;
+ }
+
+
+ XStoreName(Disp, Win, "OVR App");
+ XSetWMProtocols(Disp, Win, &Atoms[WM_DELETE_WINDOW], 1);
+
+ // Intialize empty cursor for show/hide.
+ XColor black;
+ static char noData[] = { 0,0,0,0,0,0,0,0 };
+ black.red = black.green = black.blue = 0;
+
+ Pixmap bitmapNoData = XCreateBitmapFromData(Disp, Win, noData, 8, 8);
+ InvisibleCursor = XCreatePixmapCursor(Disp, bitmapNoData, bitmapNoData,
+ &black, &black, 0, 0);
+ XDefineCursor(Disp, Win, InvisibleCursor);
+
+ Width = w;
+ Height = h;
+
+ return true;
+}
+
+void PlatformCore::SetMouseMode(MouseMode mm)
+{
+ if (mm == MMode)
+ return;
+
+ if (Win)
+ {
+ if (mm == Mouse_Relative)
+ {
+ XWarpPointer(Disp, Win, Win, 0,0,Width,Height, Width/2, Height/2);
+ }
+ else
+ {
+ //if (MMode == Mouse_Relative)
+ // ShowCursor(TRUE);
+ }
+ }
+ MMode = mm;
+}
+
+void PlatformCore::GetWindowSize(int* w, int* h) const
+{
+ *w = Width;
+ *h = Height;
+}
+
+void PlatformCore::SetWindowTitle(const char* title)
+{
+ XStoreName(Disp, Win, title);
+}
+
+void PlatformCore::ShowWindow(bool show)
+{
+ if (show)
+ XRaiseWindow(Disp, Win);
+ else
+ XIconifyWindow(Disp, Win, 0);
+}
+
+void PlatformCore::DestroyWindow()
+{
+ if (Win)
+ XDestroyWindow(Disp, Win);
+ Win = 0;
+}
+
+
+static int KeyMap[][2] =
+{
+ { XK_BackSpace, Key_Backspace },
+ { XK_Tab, Key_Tab },
+ { XK_Clear, Key_Clear },
+ { XK_Return, Key_Return },
+ { XK_Shift_L, Key_Shift },
+ { XK_Control_L, Key_Control },
+ { XK_Alt_L, Key_Alt },
+ { XK_Shift_R, Key_Shift },
+ { XK_Control_R, Key_Control },
+ { XK_Alt_R, Key_Alt },
+ { XK_Pause, Key_Pause },
+ { XK_Caps_Lock, Key_CapsLock },
+ { XK_Escape, Key_Escape },
+ { XK_space, Key_Space },
+ { XK_Page_Up, Key_PageUp },
+ { XK_Page_Down, Key_PageDown },
+ { XK_Prior, Key_PageUp },
+ { XK_Next, Key_PageDown },
+ { XK_End, Key_End },
+ { XK_Home, Key_Home },
+ { XK_Left, Key_Left },
+ { XK_Up, Key_Up },
+ { XK_Right, Key_Right },
+ { XK_Down, Key_Down },
+ { XK_Insert, Key_Insert },
+ { XK_Delete, Key_Delete },
+ { XK_Help, Key_Help },
+ { XK_Num_Lock, Key_NumLock },
+ { XK_Scroll_Lock, Key_ScrollLock },
+};
+
+
+static KeyCode MapXKToKeyCode(unsigned vk)
+{
+ unsigned key = Key_None;
+
+ if ((vk >= 'a') && (vk <= 'z'))
+ {
+ key = vk - 'a' + Key_A;
+ }
+ else if ((vk >= ' ') && (vk <= '~'))
+ {
+ key = vk;
+ }
+ else if ((vk >= XK_KP_0) && (vk <= XK_KP_9))
+ {
+ key = vk - XK_KP_0 + Key_KP_0;
+ }
+ else if ((vk >= XK_F1) && (vk <= XK_F15))
+ {
+ key = vk - XK_F1 + Key_F1;
+ }
+ else
+ {
+ for (unsigned i = 0; i< (sizeof(KeyMap) / sizeof(KeyMap[1])); i++)
+ {
+ if (vk == KeyMap[i][0])
+ {
+ key = KeyMap[i][1];
+ break;
+ }
+ }
+ }
+
+ return (KeyCode)key;
+}
+
+static int MapModifiers(int xmod)
+{
+ int mod = 0;
+ if (xmod & ShiftMask)
+ mod |= Mod_Shift;
+ if (xmod & ControlMask)
+ mod |= Mod_Control;
+ if (xmod & Mod1Mask)
+ mod |= Mod_Alt;
+ if (xmod & Mod4Mask)
+ mod |= Mod_Meta;
+ return mod;
+}
+
+void PlatformCore::processEvent(XEvent& event)
+{
+ switch (event.xany.type)
+ {
+ case ConfigureNotify:
+ if (event.xconfigure.width != Width || event.xconfigure.height != Height)
+ {
+ Width = event.xconfigure.width;
+ Height = event.xconfigure.height;
+ pApp->OnResize(Width, Height);
+
+ if (pRender)
+ pRender->SetWindowSize(Width, Height);
+ }
+ break;
+
+ case KeyPress:
+ case KeyRelease:
+ {
+ char chars[8] = {0};
+ KeySym xk;
+ XComposeStatus comp;
+ XLookupString(&event.xkey, chars, sizeof(chars), &xk, &comp);
+ if (xk != XK_VoidSymbol)
+ pApp->OnKey(MapXKToKeyCode((unsigned)xk), chars[0], event.xany.type == KeyPress, MapModifiers(event.xkey.state));
+ if (xk == XK_Escape && MMode == Mouse_Relative)
+ {
+ //ungrab
+ MMode = Mouse_RelativeEscaped;
+ showCursor(true);
+ }
+ }
+ break;
+
+ case MotionNotify:
+ if (MMode == Mouse_Relative)
+ {
+ int dx = event.xmotion.x - Width/2;
+ int dy = event.xmotion.y - Height/2;
+
+ // do not remove this check, WarpPointer generates events too.
+ if (dx == 0 && dy == 0)
+ break;
+
+ XWarpPointer(Disp, Win, Win, 0,0,Width,Height, Width/2, Height/2);
+ pApp->OnMouseMove(dx, dy, Mod_MouseRelative|MapModifiers(event.xmotion.state));
+ }
+ else
+ {
+ pApp->OnMouseMove(event.xmotion.x, event.xmotion.y, MapModifiers(event.xmotion.state));
+ }
+ break;
+
+ case MapNotify:
+ if (MMode == Mouse_Relative)
+ {
+ XWarpPointer(Disp, Win, Win, 0,0,Width,Height, Width/2, Height/2);
+ showCursor(false);
+ }
+ break;
+
+ case ButtonPress:
+ if (event.xbutton.button == 1)
+ {
+ //grab
+
+ if (MMode == Mouse_RelativeEscaped)
+ {
+ XWarpPointer(Disp, Win, Win, 0,0,Width,Height, Width/2, Height/2);
+ showCursor(false);
+ MMode = Mouse_Relative;
+ }
+ }
+ break;
+
+ case FocusOut:
+ if (MMode == Mouse_Relative)
+ {
+ MMode = Mouse_RelativeEscaped;
+ showCursor(true);
+ }
+ break;
+
+ case ClientMessage:
+ if (event.xclient.message_type == Atoms[WM_PROTOCOLS] &&
+ Atom(event.xclient.data.l[0]) == Atoms[WM_DELETE_WINDOW])
+ pApp->OnQuitRequest();
+ break;
+ }
+}
+
+int PlatformCore::Run()
+{
+ while (!Quit)
+ {
+ if (XPending(Disp))
+ {
+ XEvent event;
+ XNextEvent(Disp, &event);
+
+ if (pApp && event.xany.window == Win)
+ processEvent(event);
+ }
+ else
+ {
+ pApp->OnIdle();
+ }
+ }
+
+ return ExitCode;
+}
+
+bool PlatformCore::determineScreenOffset(int screenId, int* screenOffsetX, int* screenOffsetY)
+{
+ Display* display = XOpenDisplay(NULL);
+
+ bool foundScreen = false;
+
+ if (display)
+ {
+ int numberOfScreens;
+ XineramaScreenInfo* screens = XineramaQueryScreens(display, &numberOfScreens);
+
+ if (screenId < numberOfScreens)
+ {
+ XineramaScreenInfo screenInfo = screens[screenId];
+ *screenOffsetX = screenInfo.x_org;
+ *screenOffsetY = screenInfo.y_org;
+
+ foundScreen = true;
+ }
+
+ XFree(screens);
+ }
+
+ return foundScreen;
+}
+
+void PlatformCore::showWindowDecorations(bool show)
+{
+ // Declaration of 'MOTIF_WM_HINTS' struct and flags can be found at:
+ // https://people.gnome.org/~tthurman/docs/metacity/xprops_8h-source.html
+ typedef struct WMHints
+ {
+ unsigned long flags;
+ unsigned long functions;
+ unsigned long decorations;
+ long inputMode;
+ unsigned long status;
+ } Hints;
+
+ #define MWM_DECOR_ALL (1L << 0)
+ #define MWM_DECOR_BORDER (1L << 1)
+ #define MWM_DECOR_RESIZEH (1L << 2)
+ #define MWM_DECOR_TITLE (1L << 3)
+ #define MWM_DECOR_MENU (1L << 4)
+ #define MWM_DECOR_MINIMIZE (1L << 5)
+ #define MWM_DECOR_MAXIMIZE (1L << 6)
+
+ Atom property = XInternAtom(Disp, "_MOTIF_WM_HINTS", true);
+
+ Hints hints;
+ hints.flags = 2; // We only want to specify decoration.
+
+ if (show)
+ {
+ hints.decorations = MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MENU | MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE;
+ }
+ else
+ {
+ // Remove all window border items.
+ hints.decorations = 0;
+ }
+
+ XChangeProperty(Disp,Win,property,property,32,PropModeReplace,(unsigned char *)&hints,5);
+}
+
+int PlatformCore::IndexOf(Render::DisplayId id)
+{
+ RROutput primaryOutput = XRRGetOutputPrimary(Disp, DefaultRootWindow(Disp));
+ if (id.CgDisplayId == 0 && primaryOutput != 0)
+ return 0;
+
+ int index = (primaryOutput != 0) ? 0 : -1;
+ XRRScreenResources *screen = XRRGetScreenResources(Disp, Win);
+ for (int i = 0; i < screen->noutput && i <= id.CgDisplayId; ++i)
+ {
+ RROutput output = screen->outputs[i];
+ MonitorInfo * mi = read_edid_data(Disp, output);
+ if (mi != NULL && (primaryOutput == 0 || output != primaryOutput))
+ ++index;
+
+ delete mi;
+ }
+ XRRFreeScreenResources(screen);
+
+ return index;
+}
+
+bool PlatformCore::SetFullscreen(const Render::RendererParams& rp, int fullscreen)
+{
+ if (fullscreen == pRender->GetParams().Fullscreen)
+ return false;
+
+ int displayIndex = IndexOf(rp.Display);
+ OVR_DEBUG_LOG(("Display %d has index %d", rp.Display.CgDisplayId, displayIndex));
+
+ if (pRender->GetParams().Fullscreen == Render::Display_Window)
+ {
+ // Save the original window size and position so we can restore later.
+
+ XWindowAttributes xwa;
+ XGetWindowAttributes(Disp, Win, &xwa);
+ int x, y;
+ Window unused;
+ XTranslateCoordinates(Disp, Win, DefaultRootWindow(Disp), xwa.x, xwa.y, &x, &y, &unused);
+
+ StartVP.w = Width;
+ StartVP.h = Height;
+ StartVP.x = x;
+ StartVP.y = y;
+ }
+ else if (pRender->GetParams().Fullscreen == Render::Display_Fullscreen)
+ {
+// if (StartMode == NULL)
+// return true;
+
+// XF86VidModeSwitchToMode(Disp, 0, StartMode);
+// XF86VidModeSetViewPort(Disp, Win, 0, 0);
+ {
+ XEvent xev;
+ memset(&xev, 0, sizeof(xev));
+
+ xev.type = ClientMessage;
+ xev.xclient.window = Win;
+ xev.xclient.message_type = XInternAtom( Disp, "_NET_WM_STATE", False);
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = 0;
+ xev.xclient.data.l[1] = XInternAtom( Disp, "_NET_WM_STATE_FULLSCREEN", False);
+ xev.xclient.data.l[2] = 0;
+
+ XSendEvent( Disp, DefaultRootWindow( Disp ), False, SubstructureNotifyMask, &xev);
+ }
+
+// XWindowAttributes windowattr;
+// XGetWindowAttributes(Disp, Win, &windowattr);
+// Width = windowattr.width;
+// Height = windowattr.height;
+// pApp->OnResize(Width, Height);
+
+// if (pRender)
+// pRender->SetWindowSize(Width, Height);
+
+ XWindowChanges wc;
+ wc.width = StartVP.w;
+ wc.height = StartVP.h;
+ wc.x = StartVP.x;
+ wc.y = StartVP.y;
+
+ XConfigureWindow(Disp, Win, CWWidth | CWHeight | CWX | CWY, &wc);
+
+ showWindowDecorations(false);
+ }
+
+ if (fullscreen == Render::Display_FakeFullscreen)
+ {
+ // Transitioning from windowed to fake fullscreen.
+ int xOffset;
+ int yOffset;
+
+ if (!determineScreenOffset(displayIndex, &xOffset, &yOffset))
+ {
+ return false;
+ }
+
+ showWindowDecorations(false);
+
+ XMoveWindow(Disp, Win, xOffset, yOffset);
+ XMapRaised(Disp, Win);
+ }
+ else if (fullscreen == Render::Display_Window)
+ {
+ // Transitioning from fake fullscreen to windowed.
+ showWindowDecorations(true);
+
+ XMoveWindow(Disp, Win, StartVP.x, StartVP.y);
+ XMapRaised(Disp, Win);
+ }
+ else if (fullscreen == Render::Display_Fullscreen)
+ {
+ // Move, size, and decorate the window for fullscreen.
+
+ int xOffset;
+ int yOffset;
+
+ if (!determineScreenOffset(displayIndex, &xOffset, &yOffset))
+ return false;
+
+ showWindowDecorations(false);
+
+ XWindowChanges wc;
+ wc.x = xOffset;
+ wc.y = yOffset;
+ wc.stack_mode = 0;
+
+ XConfigureWindow(Disp, Win, CWX | CWY | CWStackMode, &wc);
+
+ // Change the display mode.
+
+// XF86VidModeModeInfo **modes = NULL;
+// int modeNum = 0;
+// if (!XF86VidModeGetAllModeLines(Disp, 0, &modeNum, &modes))
+// return false;
+
+// OVR_ASSERT(modeNum > 0);
+// StartMode = modes[0];
+
+// int bestMode = -1;
+// for (int i = 0; i < modeNum; i++)
+// {
+// if ((modes[i]->hdisplay == Width) && (modes[i]->vdisplay == Height))
+// bestMode = i;
+// }
+
+// if (bestMode == -1)
+// return false;
+
+// XF86VidModeSwitchToMode(Disp, 0, modes[bestMode]);
+ //XF86VidModeSetViewPort(Disp, Win, 0, 0);
+
+ // Make the window fullscreen in the window manager.
+
+ {
+ XEvent xev;
+ memset(&xev, 0, sizeof(xev));
+
+ xev.type = ClientMessage;
+ xev.xclient.window = Win;
+ xev.xclient.message_type = XInternAtom( Disp, "_NET_WM_STATE", False);
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = 1;
+ xev.xclient.data.l[1] = XInternAtom( Disp, "_NET_WM_STATE_FULLSCREEN", False);
+ xev.xclient.data.l[2] = 0;
+
+ XSendEvent( Disp, DefaultRootWindow( Disp ), False, SubstructureNotifyMask, &xev);
+ }
+ }
+
+ XMapRaised(Disp, Win);
+
+ Platform::PlatformCore::SetFullscreen(rp, fullscreen);
+ return true;
+}
+
+int PlatformCore::GetDisplayCount()
+{
+ int numberOfScreens = 0;
+ XineramaScreenInfo* screens = XineramaQueryScreens(Disp, &numberOfScreens);
+ XFree(screens);
+ return numberOfScreens;
+}
+
+Render::DisplayId PlatformCore::GetDisplay(int screenId)
+{
+ char device_id[32] = "";
+ int displayId = 0;
+
+ int screensPassed = 0;
+
+ RROutput primaryOutput = XRRGetOutputPrimary(Disp, DefaultRootWindow(Disp));
+
+ XRRScreenResources *screen = XRRGetScreenResources(Disp, Win);
+ for (int i = 0; i < screen->noutput; ++i)
+ {
+ RROutput output = screen->outputs[i];
+ MonitorInfo * mi = read_edid_data(Disp, output);
+ if (mi == NULL)
+ continue;
+
+ bool isMyScreen =
+ (primaryOutput == 0) ? screensPassed++ > screenId :
+ (output == primaryOutput) ? screenId == 0 :
+ ++screensPassed >= screenId;
+ if (isMyScreen)
+ {
+ OVR_sprintf(device_id, 32, "%s%04d", mi->manufacturer_code, mi->product_code);
+ displayId = (screenId == 0 && primaryOutput != 0) ? 0 : i; // Require 0 to return the primary display
+ delete mi;
+ break;
+ }
+
+ delete mi;
+ }
+ XRRFreeScreenResources(screen);
+
+ return Render::DisplayId(device_id, displayId);
+}
+
+RenderDevice* PlatformCore::SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc,
+ const char* type, const Render::RendererParams& rp)
+{
+ const SetupGraphicsDeviceSet* setupDesc = setupGraphicsDesc.PickSetupDevice(type);
+ OVR_ASSERT(setupDesc);
+
+ pRender = *setupDesc->pCreateDevice(rp, this);
+ if (pRender)
+ pRender->SetWindowSize(Width, Height);
+
+ return pRender.GetPtr();
+}
+
+void PlatformCore::showCursor(bool show)
+{
+ if (show)
+ {
+ XUndefineCursor(Disp, Win);
+ }
+ else
+ {
+ XDefineCursor(Disp, Win, InvisibleCursor);
+ }
+}
+
+}}
+
+// GL
+namespace Render { namespace GL { namespace Linux {
+
+ovrRenderAPIConfig RenderDevice::Get_ovrRenderAPIConfig() const
+{
+ static ovrGLConfig cfg;
+ cfg.OGL.Header.API = ovrRenderAPI_OpenGL;
+ cfg.OGL.Header.RTSize = Sizei(WindowWidth, WindowHeight);
+ cfg.OGL.Header.Multisample = Params.Multisample;
+ cfg.OGL.Win = Win;
+ cfg.OGL.Disp = Disp;
+ return cfg.Config;
+}
+
+Render::RenderDevice* RenderDevice::CreateDevice(const RendererParams& rp, void* oswnd)
+{
+ Platform::Linux::PlatformCore* PC = (Platform::Linux::PlatformCore*)oswnd;
+
+ GLXContext context = glXCreateContext(PC->Disp, PC->Vis, 0, GL_TRUE);
+
+ if (!context)
+ return NULL;
+
+ if (!glXMakeCurrent(PC->Disp, PC->Win, context))
+ {
+ glXDestroyContext(PC->Disp, context);
+ return NULL;
+ }
+
+ XMapRaised(PC->Disp, PC->Win);
+
+ InitGLExtensions();
+
+ XSync(PC->Disp, 0);
+
+ for (XWindowAttributes attribute; attribute.map_state != IsViewable; )
+ XGetWindowAttributes(PC->Disp,PC->Win,&attribute);
+
+ XSetInputFocus(PC->Disp, PC->Win, RevertToParent, CurrentTime);
+
+ return new Render::GL::Linux::RenderDevice(rp, PC->Disp, PC->Win, context);
+}
+
+void RenderDevice::Present(bool withVsync)
+{
+ unsigned swapInterval = (withVsync) ? 1 : 0;
+ GLuint currentSwapInterval = 0;
+ glXQueryDrawable(Disp, Win, GLX_SWAP_INTERVAL_EXT, &currentSwapInterval);
+ if (currentSwapInterval != swapInterval)
+ glXSwapIntervalEXT(Disp, Win, swapInterval);
+
+ glXSwapBuffers(Disp, Win);
+}
+
+void RenderDevice::Shutdown()
+{
+ if (Context)
+ {
+ glXMakeCurrent(Disp, 0, NULL);
+ glXDestroyContext(Disp, Context);
+ Context = NULL;
+ Win = 0;
+ }
+}
+
+}}}}
+
+
+int main(int argc, const char* argv[])
+{
+ using namespace OVR;
+ using namespace OVR::Platform;
+
+ // CreateApplication must be the first call since it does OVR::System::Initialize.
+ Application* app = Application::CreateApplication();
+ Linux::PlatformCore* platform = new Linux::PlatformCore(app);
+ // The platform attached to an app will be deleted by DestroyApplication.
+ app->SetPlatformCore(platform);
+
+ int exitCode = app->OnStartup(argc, argv);
+ if (!exitCode)
+ exitCode = platform->Run();
+
+ // No OVR functions involving memory are allowed after this.
+ Application::DestroyApplication(app);
+ app = 0;
+
+ return exitCode;
+}
diff --git a/Samples/CommonSrc/Platform/Linux_Platform.h b/Samples/CommonSrc/Platform/Linux_Platform.h
new file mode 100644
index 0000000..f2e438e
--- /dev/null
+++ b/Samples/CommonSrc/Platform/Linux_Platform.h
@@ -0,0 +1,147 @@
+/************************************************************************************
+
+Filename : Platform_Linux.h
+Content : Linux (X11) implementation of Platform app infrastructure
+Created : September 6, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Platform_Linux_h
+#define OVR_Platform_Linux_h
+
+#include "Platform.h"
+#include "../Render/Render_GL_Device.h"
+
+#include <GL/glx.h>
+#include <X11/extensions/xf86vmode.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+namespace OVR { namespace Render {
+ class RenderDevice;
+}}
+
+namespace OVR { namespace Platform { namespace Linux {
+
+class PlatformCore : public Platform::PlatformCore
+{
+ XF86VidModeModeInfo* StartMode;
+ Recti StartVP;
+
+ int IndexOf(Render::DisplayId id);
+
+public:
+ Display* Disp;
+ XVisualInfo* Vis;
+ Window Win;
+
+ bool Quit;
+ int ExitCode;
+ int Width, Height;
+
+ MouseMode MMode;
+ Cursor InvisibleCursor;
+
+ enum
+ {
+ WM_PROTOCOLS,
+ WM_DELETE_WINDOW,
+ NumAtoms
+ };
+ Atom Atoms[NumAtoms];
+
+ void processEvent(XEvent& event);
+
+ Render::RenderDevice* SetupGraphics_GL(const Render::RendererParams& rp);
+
+ void showCursor(bool show);
+ bool determineScreenOffset(int screenId, int* screenOffsetX, int* screenOffsetY);
+ void showWindowDecorations(bool show);
+
+public:
+ PlatformCore(Application* app);
+ ~PlatformCore();
+
+ bool SetupWindow(int w, int h);
+ void Exit(int exitcode) { Quit = 1; ExitCode = exitcode; }
+
+ RenderDevice* SetupGraphics(const SetupGraphicsDeviceSet& setupGraphicsDesc,
+ const char* gtype, const Render::RendererParams& rp);
+
+ void SetMouseMode(MouseMode mm);
+ void GetWindowSize(int* w, int* h) const;
+
+ void SetWindowTitle(const char*title);
+
+ void ShowWindow(bool show);
+ void DestroyWindow();
+ bool SetFullscreen(const Render::RendererParams& rp, int fullscreen);
+ int GetDisplayCount();
+ Render::DisplayId GetDisplay(int screen);
+
+ int Run();
+};
+
+}}
+namespace Render { namespace GL { namespace Linux {
+
+class RenderDevice : public Render::GL::RenderDevice
+{
+ Display* Disp;
+ Window Win;
+ GLXContext Context;
+
+public:
+ RenderDevice(const Render::RendererParams& p, Display* disp, Window w, GLXContext gl)
+ : GL::RenderDevice(p), Disp(disp), Win(w), Context(gl) {}
+
+ virtual void Shutdown();
+ virtual void Present(bool withVsync);
+ virtual ovrRenderAPIConfig Get_ovrRenderAPIConfig() const;
+
+ // oswnd = Linux::PlatformCore*
+ static Render::RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd);
+};
+
+}}}}
+
+
+// OVR_PLATFORM_APP_ARGS specifies the Application class to use for startup,
+// providing it with startup arguments.
+#define OVR_PLATFORM_APP_ARGS(AppClass, args) \
+ OVR::Platform::Application* OVR::Platform::Application::CreateApplication() \
+ { OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); \
+ return new AppClass args; } \
+ void OVR::Platform::Application::DestroyApplication(OVR::Platform::Application* app) \
+ { OVR::Platform::PlatformCore* platform = app->pPlatform; \
+ delete app; delete platform; OVR::System::Destroy(); };
+
+// OVR_PLATFORM_APP_ARGS specifies the Application startup class with no args.
+#define OVR_PLATFORM_APP(AppClass) OVR_PLATFORM_APP_ARGS(AppClass, ())
+
+#define OVR_PLATFORM_APP_ARGS_WITH_LOG(AppClass, LogClass, args) \
+ OVR::Platform::Application* OVR::Platform::Application::CreateApplication() \
+ { static LogClass log; OVR::System::Init(&log); \
+ return new AppClass args; } \
+ void OVR::Platform::Application::DestroyApplication(OVR::Platform::Application* app) \
+ { OVR::Platform::PlatformCore* platform = app->pPlatform; \
+ delete app; delete platform; OVR::System::Destroy(); };
+
+#define OVR_PLATFORM_APP_WITH_LOG(AppClass,LogClass) OVR_PLATFORM_APP_ARGS_WITH_LOG(AppClass,LogClass, ())
+
+#endif
diff --git a/Samples/OculusWorldDemo/Makefile b/Samples/OculusWorldDemo/Makefile
new file mode 100644
index 0000000..91ead29
--- /dev/null
+++ b/Samples/OculusWorldDemo/Makefile
@@ -0,0 +1,153 @@
+#############################################################################
+#
+# Filename : Makefile
+# Content : Makefile for building linux OculusWorldDemo
+# Created : 2013
+# Authors : Simon Hallam and Peter Giokaris
+# Copyright : Copyright 2013 OculusVR, Inc. All Rights Reserved
+# Instruction : The g++ compiler and stdndard lib packages need to be
+# installed on the system. Navigate in a shell to the
+# directory where this Makefile is located and enter:
+#
+# make builds the release version for the
+# current architechture
+# make clean delete intermediate release object files
+# and the executabe file
+# make DEBUG=1 builds the debug version for the current
+# architechture
+# make clean DEBUG=1 deletes intermediate debug object files
+# and the executable file
+#
+# Output : Relative to the directory this Makefile lives in, executable
+# files get built at the following locations depending upon the
+# architechture of the system you are running:
+#
+# ./Release/OculusWorldDemo_i386_Release
+# ./Release/OculusWorldDemo_x86_64_Release
+# ./Release/OculusWorldDemo_i386_Debug
+# ./Release/OculusWorldDemo_x86_64_Debug
+#
+#############################################################################
+
+####### Detect system architecture
+
+SYSARCH = i386
+ifeq ($(shell uname -m),x86_64)
+SYSARCH = x86_64
+endif
+
+####### Compiler, tools and options
+
+CXX = g++
+LINK = g++
+MAKE = make
+DELETEFILE = rm -f
+DEFINES = -DQT_WEBKIT -DGL_GLEXT_PROTOTYPES
+
+####### Detect debug or release
+
+DEBUG = 0
+ifeq ($(DEBUG), 1)
+ CXXFLAGS = -pipe -DDEBUG -DOVR_BUILD_DEBUG -g $(DEFINES)
+ LFLAGS =
+ RELEASETYPE = Debug
+else
+ CXXFLAGS = -pipe -O2 $(DEFINES)
+ LFLAGS = -O1
+ RELEASETYPE = Release
+endif
+
+####### Paths
+
+LIBOVRPATH = ../../LibOVR
+COMMONSRCPATH = ../CommonSrc
+3RDPARTYPATH = ../../3rdParty
+INCPATH = -I. -I.. -I$(COMMONSRCPATH) -I$(LIBOVRPATH)/Include -I$(LIBOVRPATH)/Src
+OBJPATH = ./Obj/Linux/$(RELEASETYPE)/$(SYSARCH)
+CXX_BUILD = $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $(OBJPATH)/
+
+####### Files
+
+LIBS = -L$(LIBOVRPATH)/Lib/Linux/$(RELEASETYPE)/$(SYSARCH) \
+ -lovr \
+ -ludev \
+ -lpthread \
+ -lGL \
+ -lX11 \
+ -lXinerama \
+ -lXxf86vm \
+ -lXrandr
+
+OBJECTS = $(OBJPATH)/OculusWorldDemo.o \
+ $(OBJPATH)/OculusWorldDemo_Scene.o \
+ $(OBJPATH)/OptionMenu.o \
+ $(OBJPATH)/Player.o \
+ $(OBJPATH)/Platform.o \
+ $(OBJPATH)/RenderProfiler.o \
+ $(OBJPATH)/Linux_Platform.o \
+ $(OBJPATH)/Linux_Gamepad.o \
+ $(OBJPATH)/Render_Device.o \
+ $(OBJPATH)/Render_GL_Device.o \
+ $(OBJPATH)/Render_LoadTextureDDS.o \
+ $(OBJPATH)/Render_LoadTextureTGA.o \
+ $(OBJPATH)/Render_XmlSceneLoader.o \
+ $(OBJPATH)/tinyxml.o
+
+TARGET = ./Release/OculusWorldDemo_$(SYSARCH)_$(RELEASETYPE)
+
+####### Rules
+
+all: $(TARGET)
+
+$(TARGET): $(LIBOVRPATH)/Lib/Linux/$(RELEASETYPE)/$(SYSARCH)/libovr.a
+ $(MAKE) -C $(LIBOVRPATH) DEBUG=$(DEBUG)
+
+$(TARGET): $(OBJECTS)
+ $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS)
+
+$(OBJPATH)/OculusWorldDemo.o: OculusWorldDemo.cpp
+ $(CXX_BUILD)OculusWorldDemo.o OculusWorldDemo.cpp
+
+$(OBJPATH)/OculusWorldDemo_Scene.o: OculusWorldDemo_Scene.cpp
+ $(CXX_BUILD)OculusWorldDemo_Scene.o OculusWorldDemo_Scene.cpp
+
+$(OBJPATH)/Player.o: Player.cpp
+ $(CXX_BUILD)Player.o Player.cpp
+
+$(OBJPATH)/RenderProfiler.o: ../../Samples/CommonSrc/Util/RenderProfiler.cpp
+ $(CXX_BUILD)RenderProfiler.o ../../Samples/CommonSrc/Util/RenderProfiler.cpp
+
+$(OBJPATH)/OptionMenu.o: ../../Samples/CommonSrc/Util/OptionMenu.cpp
+ $(CXX_BUILD)OptionMenu.o ../../Samples/CommonSrc/Util/OptionMenu.cpp
+
+$(OBJPATH)/Platform.o: ../../Samples/CommonSrc/Platform/Platform.cpp
+ $(CXX_BUILD)Platform.o ../../Samples/CommonSrc/Platform/Platform.cpp
+
+$(OBJPATH)/Linux_Platform.o: ../../Samples/CommonSrc/Platform/Linux_Platform.cpp
+ $(CXX_BUILD)Linux_Platform.o ../../Samples/CommonSrc/Platform/Linux_Platform.cpp
+
+$(OBJPATH)/Linux_Gamepad.o: ../../Samples/CommonSrc/Platform/Linux_Gamepad.cpp
+ $(CXX_BUILD)Linux_Gamepad.o ../../Samples/CommonSrc/Platform/Linux_Gamepad.cpp
+
+$(OBJPATH)/Render_Device.o: ../../Samples/CommonSrc/Render/Render_Device.cpp $
+ $(CXX_BUILD)Render_Device.o ../../Samples/CommonSrc/Render/Render_Device.cpp
+
+$(OBJPATH)/Render_GL_Device.o: ../../Samples/CommonSrc/Render/Render_GL_Device.cpp
+ $(CXX_BUILD)Render_GL_Device.o ../../Samples/CommonSrc/Render/Render_GL_Device.cpp
+
+$(OBJPATH)/Render_LoadTextureDDS.o: ../../Samples/CommonSrc/Render/Render_LoadTextureDDS.cpp
+ $(CXX_BUILD)Render_LoadTextureDDS.o ../../Samples/CommonSrc/Render/Render_LoadTextureDDS.cpp
+
+$(OBJPATH)/Render_LoadTextureTGA.o: ../../Samples/CommonSrc/Render/Render_LoadTextureTGA.cpp
+ $(CXX_BUILD)Render_LoadTextureTGA.o ../../Samples/CommonSrc/Render/Render_LoadTextureTGA.cpp
+
+$(OBJPATH)/Render_XmlSceneLoader.o: ../../Samples/CommonSrc/Render/Render_XmlSceneLoader.cpp
+ $(CXX_BUILD)Render_XmlSceneLoader.o ../../Samples/CommonSrc/Render/Render_XmlSceneLoader.cpp
+
+$(OBJPATH)/tinyxml.o: ../../3rdParty/TinyXml/tinyxml2.cpp
+ $(CXX_BUILD)tinyxml.o ../../3rdParty/TinyXml/tinyxml2.cpp
+
+clean:
+ -$(DELETEFILE) $(OBJECTS)
+ -$(DELETEFILE) $(TARGET)
+