diff options
-rw-r--r-- | 3rdParty/EDID/edid.cpp | 446 | ||||
-rw-r--r-- | 3rdParty/EDID/edid.h | 174 | ||||
-rw-r--r-- | ConfigurePermissionsAndPackages.sh | 51 | ||||
-rw-r--r-- | LibOVR/90-oculus.rules | 2 | ||||
-rw-r--r-- | LibOVR/Makefile | 278 | ||||
-rw-r--r-- | LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp | 787 | ||||
-rw-r--r-- | LibOVR/Src/OVR_Linux_DeviceManager.cpp | 331 | ||||
-rw-r--r-- | LibOVR/Src/OVR_Linux_DeviceManager.h | 122 | ||||
-rw-r--r-- | LibOVR/Src/OVR_Linux_HIDDevice.cpp | 819 | ||||
-rw-r--r-- | LibOVR/Src/OVR_Linux_HIDDevice.h | 135 | ||||
-rw-r--r-- | LibOVR/Src/OVR_Linux_HMDDevice.cpp | 291 | ||||
-rw-r--r-- | LibOVR/Src/OVR_Linux_HMDDevice.h | 154 | ||||
-rw-r--r-- | LibOVR/Src/OVR_Linux_SensorDevice.cpp | 57 | ||||
-rw-r--r-- | Makefile | 82 | ||||
-rw-r--r-- | OculusConfigurationUtility.sh | 28 | ||||
-rw-r--r-- | OculusWorldDemo.sh | 25 | ||||
-rw-r--r-- | Samples/CommonSrc/Platform/Linux_Gamepad.cpp | 453 | ||||
-rw-r--r-- | Samples/CommonSrc/Platform/Linux_Gamepad.h | 83 | ||||
-rw-r--r-- | Samples/CommonSrc/Platform/Linux_Platform.cpp | 784 | ||||
-rw-r--r-- | Samples/CommonSrc/Platform/Linux_Platform.h | 147 | ||||
-rw-r--r-- | Samples/OculusWorldDemo/Makefile | 153 |
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, ¤tSwapInterval); + 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) + |