<commit_before>
#include "FlareTabView.h"
#include "../../Flare.h"
#include "../Style/FlareStyleSet.h"

/*----------------------------------------------------
	Construct
----------------------------------------------------*/

void SFlareTabView::Construct(const FArguments& InArgs)
{
	// Data
	CurrentTabIndex = 0;
	const FFlareStyleCatalog& Theme = FFlareStyleSet::GetDefaultTheme();

	// Structure
	ChildSlot
	[
		SNew(SVerticalBox)

		// Tabs
		+ SVerticalBox::Slot()
		.HAlign(HAlign_Fill)
		.VAlign(VAlign_Top)
		.AutoHeight()
		[
			SNew(SVerticalBox)

			// Header buttons
			+ SVerticalBox::Slot()
			.AutoHeight()
			[
				SNew(SHorizontalBox)

				+ SHorizontalBox::Slot()
				.AutoWidth()
				[
					SAssignNew(Header, SHorizontalBox)
				]

				+ SHorizontalBox::Slot()
				.Padding(FMargin(0, 0, 0, 1))
				[
					SNew(SImage)
					.Image(&Theme.BackgroundBrush)
				]
			]

			// Header border
			+ SVerticalBox::Slot()
			.AutoHeight()
			[
				SNew(SBox)
				.HeightOverride(1)
				[
					SNew(SImage)
					.Image(&Theme.NearInvisibleBrush)
				]
			]
		]

		// Main view
		+ SVerticalBox::Slot()
		.HAlign(HAlign_Fill)
		.Padding(Theme.ContentPadding)
		[
			SNew(SScrollBox)
			.Style(&Theme.ScrollBoxStyle)
			.ScrollBarStyle(&Theme.ScrollBarStyle)

			+ SScrollBox::Slot()
			.HAlign(HAlign_Fill)
			[
				SAssignNew(Content, SWidgetSwitcher)
				.WidgetIndex(this, &SFlareTabView::GetCurrentTabIndex)
			]
		]
	];

	// Slot contents
	int32 Index = 0;
	for (SFlareTabView::FSlot* TabSlot : InArgs.Slots)
	{
		// Add header entry
		Header->AddSlot()
		.AutoWidth()
		[
			SNew(SFlareButton)
			.Text(TabSlot->HeaderText)
			.HelpText(TabSlot->HeaderHelpText)
			.OnClicked(this, &SFlareTabView::SetCurrentTabIndex, Index)
			.IsDisabled(this, &SFlareTabView::IsCurrentTab, Index)
			.Width(5)
		];

		// Add content
		Content->AddSlot()
		[
			TabSlot->GetWidget()
		];

		Index++;
	}
}

/*----------------------------------------------------
	Callbacks
----------------------------------------------------*/

int32 SFlareTabView::GetCurrentTabIndex() const
{
	return CurrentTabIndex;
}

bool SFlareTabView::IsCurrentTab(int32 Index) const
{
	return (CurrentTabIndex == Index);
}

void SFlareTabView::SetCurrentTabIndex(int32 Index)
{
	CurrentTabIndex = Index;
}
<commit_msg>#1147 Fix unwanted margins around the company log<commit_after>
#include "FlareTabView.h"
#include "../../Flare.h"
#include "../Style/FlareStyleSet.h"

/*----------------------------------------------------
	Construct
----------------------------------------------------*/

void SFlareTabView::Construct(const FArguments& InArgs)
{
	// Data
	CurrentTabIndex = 0;
	const FFlareStyleCatalog& Theme = FFlareStyleSet::GetDefaultTheme();

	// Structure
	ChildSlot
	[
		SNew(SVerticalBox)

		// Tabs
		+ SVerticalBox::Slot()
		.HAlign(HAlign_Fill)
		.VAlign(VAlign_Top)
		.AutoHeight()
		[
			SNew(SVerticalBox)

			// Header buttons
			+ SVerticalBox::Slot()
			.AutoHeight()
			[
				SNew(SHorizontalBox)

				+ SHorizontalBox::Slot()
				.AutoWidth()
				[
					SAssignNew(Header, SHorizontalBox)
				]

				+ SHorizontalBox::Slot()
				.Padding(FMargin(0, 0, 0, 1))
				[
					SNew(SImage)
					.Image(&Theme.BackgroundBrush)
				]
			]

			// Header border
			+ SVerticalBox::Slot()
			.AutoHeight()
			[
				SNew(SBox)
				.HeightOverride(1)
				[
					SNew(SImage)
					.Image(&Theme.NearInvisibleBrush)
				]
			]
		]

		// Main view
		+ SVerticalBox::Slot()
		.HAlign(HAlign_Fill)
		[
			SNew(SScrollBox)
			.Style(&Theme.ScrollBoxStyle)
			.ScrollBarStyle(&Theme.ScrollBarStyle)

			+ SScrollBox::Slot()
			.HAlign(HAlign_Fill)
			[
				SAssignNew(Content, SWidgetSwitcher)
				.WidgetIndex(this, &SFlareTabView::GetCurrentTabIndex)
			]
		]
	];

	// Slot contents
	int32 Index = 0;
	for (SFlareTabView::FSlot* TabSlot : InArgs.Slots)
	{
		// Add header entry
		Header->AddSlot()
		.AutoWidth()
		[
			SNew(SFlareButton)
			.Text(TabSlot->HeaderText)
			.HelpText(TabSlot->HeaderHelpText)
			.OnClicked(this, &SFlareTabView::SetCurrentTabIndex, Index)
			.IsDisabled(this, &SFlareTabView::IsCurrentTab, Index)
			.Width(5)
		];

		// Add content
		Content->AddSlot()
		[
			TabSlot->GetWidget()
		];

		Index++;
	}
}

/*----------------------------------------------------
	Callbacks
----------------------------------------------------*/

int32 SFlareTabView::GetCurrentTabIndex() const
{
	return CurrentTabIndex;
}

bool SFlareTabView::IsCurrentTab(int32 Index) const
{
	return (CurrentTabIndex == Index);
}

void SFlareTabView::SetCurrentTabIndex(int32 Index)
{
	CurrentTabIndex = Index;
}
<|endoftext|>


<commit_before>#ifdef _WIN32
#  include <windows.h>
#  include <stdlib.h>
#  include <intrin.h>
#  include <stdint.h>

namespace tracy
{
bool DiscoveryAVX();
bool DiscoveryAVX2();
}

int main( int argc, char** argv );

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmd, int nCmd )
{
    {
        uint32_t regs[4];
        __cpuidex( (int*)regs, 0, 0 );
        const uint32_t maxLeaf = regs[0];
        bool cpuHasAVX = false;
        bool cpuHasAVX2 = false;
        if( maxLeaf >= 1 )
        {
            __cpuidex( (int*)regs, 1, 0 );
            cpuHasAVX = ( regs[2] & 0x10000000 ) != 0;
        }
        if( maxLeaf >= 7 )
        {
            __cpuidex( (int*)regs, 7, 0 );
            cpuHasAVX2 = ( regs[1] & 0x00000020 ) != 0;
        }

        if( tracy::DiscoveryAVX2() && !cpuHasAVX2 )
        {
            MessageBoxA( nullptr, "This program is compiled with AVX2 instruction set, but your CPU doesn't support it. You must recompile with lower instruction set.\n\nIn Visual Studio go to Project properties -> C/C++ -> Code Generation -> Enable Enhanced Instruction Set and select appropriate value for your CPU.", "Wrong CPU architecture", MB_ICONERROR );
            return 0;
        }
        if( tracy::DiscoveryAVX() && !cpuHasAVX )
        {
            MessageBoxA( nullptr, "This program is compiled with AVX instruction set, but your CPU doesn't support it. You must recompile with lower instruction set.\n\nIn Visual Studio go to Project properties -> C/C++ -> Code Generation -> Enable Enhanced Instruction Set and select appropriate value for your CPU.", "Wrong CPU architecture", MB_ICONERROR );
            return 0;
        }
    }

    return main( __argc, __argv );
}
#endif
<commit_msg>ThirdParty: Fix profiler that broke after change gone missing during update from upstream.<commit_after>#ifdef _WIN32
#  include <windows.h>
#  include <stdlib.h>
#  include <intrin.h>
#  include <stdint.h>
// Urho3D: This include is required for main function to be properly recognized.
#  include <SDL/SDL.h>

namespace tracy
{
bool DiscoveryAVX();
bool DiscoveryAVX2();
}

int main( int argc, char** argv );

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmd, int nCmd )
{
    {
        uint32_t regs[4];
        __cpuidex( (int*)regs, 0, 0 );
        const uint32_t maxLeaf = regs[0];
        bool cpuHasAVX = false;
        bool cpuHasAVX2 = false;
        if( maxLeaf >= 1 )
        {
            __cpuidex( (int*)regs, 1, 0 );
            cpuHasAVX = ( regs[2] & 0x10000000 ) != 0;
        }
        if( maxLeaf >= 7 )
        {
            __cpuidex( (int*)regs, 7, 0 );
            cpuHasAVX2 = ( regs[1] & 0x00000020 ) != 0;
        }

        if( tracy::DiscoveryAVX2() && !cpuHasAVX2 )
        {
            MessageBoxA( nullptr, "This program is compiled with AVX2 instruction set, but your CPU doesn't support it. You must recompile with lower instruction set.\n\nIn Visual Studio go to Project properties -> C/C++ -> Code Generation -> Enable Enhanced Instruction Set and select appropriate value for your CPU.", "Wrong CPU architecture", MB_ICONERROR );
            return 0;
        }
        if( tracy::DiscoveryAVX() && !cpuHasAVX )
        {
            MessageBoxA( nullptr, "This program is compiled with AVX instruction set, but your CPU doesn't support it. You must recompile with lower instruction set.\n\nIn Visual Studio go to Project properties -> C/C++ -> Code Generation -> Enable Enhanced Instruction Set and select appropriate value for your CPU.", "Wrong CPU architecture", MB_ICONERROR );
            return 0;
        }
    }

    return main( __argc, __argv );
}
#endif
<|endoftext|>


<commit_before>#include "dsa_common.h"

#include "date_time.h"

#include <chrono>
#include <iomanip>
#include <sstream>

namespace dsa {

thread_local std::chrono::milliseconds _last_ms{0};
thread_local std::string _last_ts;

static void update_ts(std::chrono::system_clock::time_point now) {
  std::time_t t = std::chrono::system_clock::to_time_t(now);
  std::stringstream ss;

  ss << std::put_time(std::localtime(&t), "%FT%T.000%z0");
  std::string str = ss.str();

  // set milli second;
  auto ms = _last_ms.count() % 1000;
  str[20] = static_cast<char>((ms / 100) + '0');
  str[21] = static_cast<char>(((ms % 100) / 10) + '0');
  str[22] = static_cast<char>((ms % 10) + '0');

  // fix format of timezone;
  str[28] = str[27];
  str[27] = str[26];
  str[26] = ':';

  _last_ts = std::move(str);
}

const std::string& DateTime::get_ts() {
  // only generate the string when time changed
  auto now = std::chrono::system_clock::now();
  auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
      now.time_since_epoch());
  if (ms != _last_ms) {
    _last_ms = ms;
    update_ts(now);
  }
  return _last_ts;
}
}
<commit_msg>make DateTime thread safe<commit_after>#include "dsa_common.h"

#include "date_time.h"

#include <chrono>
#include <iomanip>
#include <sstream>

namespace dsa {

thread_local std::chrono::milliseconds _last_ms{0};
thread_local std::string _last_ts;

static void update_ts(std::chrono::system_clock::time_point now) {
  std::time_t t = std::chrono::system_clock::to_time_t(now);
  std::stringstream ss;

  tm localt;
  localtime_r(&t, &localt);
  ss << std::put_time(&localt, "%FT%T.000%z0");

  std::string str = ss.str();

  // set milli second;
  auto ms = _last_ms.count() % 1000;
  str[20] = static_cast<char>((ms / 100) + '0');
  str[21] = static_cast<char>(((ms % 100) / 10) + '0');
  str[22] = static_cast<char>((ms % 10) + '0');

  // fix format of timezone;
  str[28] = str[27];
  str[27] = str[26];
  str[26] = ':';

  _last_ts = std::move(str);
}

const std::string& DateTime::get_ts() {
  // only generate the string when time changed
  auto now = std::chrono::system_clock::now();
  auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
      now.time_since_epoch());
  if (ms != _last_ms) {
    _last_ms = ms;
    update_ts(now);
  }
  return _last_ts;
}
}
<|endoftext|>


<commit_before>#pragma once

// `krbn::hid_manager` can be used safely in a multi-threaded environment.

#include "boost_utility.hpp"
#include "cf_utility.hpp"
#include "device_detail.hpp"
#include "dispatcher.hpp"
#include "human_interface_device.hpp"
#include "logger.hpp"
#include "types.hpp"
#include <IOKit/hid/IOHIDManager.h>
#include <unordered_map>

namespace krbn {
class hid_manager final : public pqrs::dispatcher::extra::dispatcher_client {
public:
  // Signals (invoked from the shared dispatcher thread)

  // Return false to ignore device.
  boost::signals2::signal<bool(IOHIDDeviceRef _Nonnull), boost_utility::signals2_combiner_call_while_true> device_detecting;
  boost::signals2::signal<void(std::weak_ptr<human_interface_device>)> device_detected;
  boost::signals2::signal<void(std::weak_ptr<human_interface_device>)> device_removed;

  // Methods

  hid_manager(const hid_manager&) = delete;

  hid_manager(const std::vector<std::pair<hid_usage_page, hid_usage>>& usage_pairs) : dispatcher_client(),
                                                                                usage_pairs_(usage_pairs),
                                                                                manager_(nullptr),
                                                                                refresh_timer_(*this) {
    run_loop_thread_ = std::make_unique<cf_utility::run_loop_thread>();
  }

  virtual ~hid_manager(void) {
    detach_from_dispatcher([this] {
      stop();
    });

    run_loop_thread_->terminate();
    run_loop_thread_ = nullptr;
  }

  void async_start(void) {
    enqueue_to_dispatcher([this] {
      start();
    });
  }

  void async_stop(void) {
    enqueue_to_dispatcher([this] {
      stop();
    });
  }

  std::vector<std::shared_ptr<human_interface_device>> copy_hids(void) const {
    std::lock_guard<std::mutex> lock(hids_mutex_);

    return hids_;
  }

private:
  // This method is executed in the dispatcher thread.
  void start(void) {
    if (manager_) {
      logger::get_logger().warn("hid_manager is already started.");
      return;
    }

    manager_ = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
    if (!manager_) {
      logger::get_logger().error("IOHIDManagerCreate is failed.");
      return;
    }

    if (auto device_matching_dictionaries = iokit_utility::create_device_matching_dictionaries(usage_pairs_)) {
      IOHIDManagerSetDeviceMatchingMultiple(manager_, device_matching_dictionaries);
      CFRelease(device_matching_dictionaries);
    }

    IOHIDManagerRegisterDeviceMatchingCallback(manager_, static_device_matching_callback, this);
    IOHIDManagerRegisterDeviceRemovalCallback(manager_, static_device_removal_callback, this);

    IOHIDManagerScheduleWithRunLoop(manager_,
                                    run_loop_thread_->get_run_loop(),
                                    kCFRunLoopCommonModes);

    // We need to call `wake` after `IOHIDManagerScheduleWithRunLoop`.

    run_loop_thread_->wake();

    refresh_timer_.start(
        [this] {
          refresh_if_needed();
        },
        std::chrono::milliseconds(5000));

    logger::get_logger().info("hid_manager is started.");
  }

  // This method is executed in the dispatcher thread.
  void stop(void) {
    if (!manager_) {
      return;
    }

    // refresh_timer_

    refresh_timer_.stop();

    // manager_

    IOHIDManagerUnscheduleFromRunLoop(manager_,
                                      run_loop_thread_->get_run_loop(),
                                      kCFRunLoopCommonModes);
    CFRelease(manager_);
    manager_ = nullptr;

    // Other variables

    registry_entry_ids_.clear();

    {
      std::lock_guard<std::mutex> lock(hids_mutex_);

      hids_.clear();
    }

    logger::get_logger().info("hid_manager is stopped.");
  }

  // This method is executed in the dispatcher thread.
  void refresh_if_needed(void) {
    // `device_removal_callback` is sometimes missed
    // and invalid human_interface_devices are remained into hids_.
    // (The problem is likely occur just after wake up from sleep.)
    //
    // We validate the human_interface_device,
    // and then reload devices if there is an invalid human_interface_device.

    bool needs_refresh = false;

    {
      std::lock_guard<std::mutex> lock(hids_mutex_);

      for (const auto& hid : hids_) {
        if (!hid->validate()) {
          logger::get_logger().warn("Refreshing hid_manager since a dangling human_interface_device is found. ({0})",
                                    hid->get_name_for_log());
          needs_refresh = true;
          break;
        }
      }
    }

    if (needs_refresh) {
      stop();
      start();
    }
  }

  boost::optional<registry_entry_id> find_registry_entry_id(IOHIDDeviceRef _Nonnull device) {
    auto it = registry_entry_ids_.find(device);
    if (it != std::end(registry_entry_ids_)) {
      return it->second;
    }
    return boost::none;
  }

  static void static_device_matching_callback(void* _Nullable context,
                                              IOReturn result,
                                              void* _Nullable sender,
                                              IOHIDDeviceRef _Nonnull device) {
    if (result != kIOReturnSuccess) {
      return;
    }

    auto self = static_cast<hid_manager*>(context);
    if (!self) {
      return;
    }

    self->device_matching_callback(device);
  }

  void device_matching_callback(IOHIDDeviceRef _Nonnull device) {
    // `static_device_matching_callback` is called by multiple threads.
    // (It is not called from `run_loop_thread_`.)
    // Thus, we have to synchronize by using `dispatcher`.

    if (!device) {
      return;
    }

    CFRetain(device);

    enqueue_to_dispatcher([this, device] {
      if (!device_detecting(device)) {
        goto finish;
      }

      if (auto registry_entry_id = iokit_utility::find_registry_entry_id(device)) {
        // iokit_utility::find_registry_entry_id will be failed in device_removal_callback at least on macOS 10.13.
        // Thus, we have to memory device and registry_entry_id correspondence by myself.
        registry_entry_ids_[device] = *registry_entry_id;

        // Skip if same device is already matched.
        // (Multiple usage device (e.g. usage::pointer and usage::mouse) will be matched twice.)

        std::shared_ptr<human_interface_device> hid;

        {
          std::lock_guard<std::mutex> lock(hids_mutex_);

          if (std::any_of(std::begin(hids_),
                          std::end(hids_),
                          [&](auto&& h) {
                            return *registry_entry_id == h->get_registry_entry_id();
                          })) {
            // logger::get_logger().info("registry_entry_id:{0} already exists", static_cast<uint64_t>(*registry_entry_id));
            goto finish;
          }

          hid = std::make_shared<human_interface_device>(device, *registry_entry_id);
          hids_.push_back(hid);
        }

        enqueue_to_dispatcher([this, hid] {
          device_detected(hid);
        });
      }

    finish:
      CFRelease(device);
    });
  }

  static void static_device_removal_callback(void* _Nullable context,
                                             IOReturn result,
                                             void* _Nullable sender,
                                             IOHIDDeviceRef _Nonnull device) {
    if (result != kIOReturnSuccess) {
      return;
    }

    auto self = static_cast<hid_manager*>(context);
    if (!self) {
      return;
    }

    self->device_removal_callback(device);
  }

  void device_removal_callback(IOHIDDeviceRef _Nonnull device) {
    // `static_device_removal_callback` is called by multiple threads.
    // (It is not called from `run_loop_thread_`.)
    // Thus, we have to synchronize by using `dispatcher`.

    if (!device) {
      return;
    }

    CFRetain(device);

    enqueue_to_dispatcher([this, device] {
      if (auto registry_entry_id = find_registry_entry_id(device)) {
        std::shared_ptr<human_interface_device> hid;

        {
          std::lock_guard<std::mutex> lock(hids_mutex_);

          auto it = std::find_if(std::begin(hids_),
                                 std::end(hids_),
                                 [&](auto&& h) {
                                   return *registry_entry_id == h->get_registry_entry_id();
                                 });
          if (it != hids_.end()) {
            hid = *it;
            hids_.erase(it);
          }
        }

        if (hid) {
          hid->set_removed();

          enqueue_to_dispatcher([this, hid] {
            device_removed(hid);
          });
        }

        // There might be multiple devices for one registry_entry_id.
        // (For example, keyboard and mouse combined device.)
        // And there is possibility that removal callback is not called with some device.
        //
        // For example, there are 3 devices for 1 registry_entry_id (4294974284).
        //   - device1 { device:0x7fb9d64078a0 => registry_entry_id:4294974284 }
        //   - device2 { device:0x7fb9d8301390 => registry_entry_id:4294974284 }
        //   - device3 { device:0x7fb9d830a630 => registry_entry_id:4294974284 }
        // And device_removal_callback are called only with device1 and device2.
        //
        // We should remove device1, device2 and device3 at the same time in order to deal with this case.

        for (auto it = std::begin(registry_entry_ids_); it != std::end(registry_entry_ids_);) {
          if (it->second == *registry_entry_id) {
            it = registry_entry_ids_.erase(it);
          } else {
            std::advance(it, 1);
          }
        }
      }

      CFRelease(device);
    });
  }

  std::unique_ptr<cf_utility::run_loop_thread> run_loop_thread_;

  std::vector<std::pair<hid_usage_page, hid_usage>> usage_pairs_;

  IOHIDManagerRef _Nullable manager_;
  pqrs::dispatcher::extra::timer refresh_timer_;

  std::unordered_map<IOHIDDeviceRef, registry_entry_id> registry_entry_ids_;

  std::vector<std::shared_ptr<human_interface_device>> hids_;
  mutable std::mutex hids_mutex_;
};
} // namespace krbn
<commit_msg>fix hid_manager<commit_after>#pragma once

// `krbn::hid_manager` can be used safely in a multi-threaded environment.

#include "boost_utility.hpp"
#include "cf_utility.hpp"
#include "device_detail.hpp"
#include "dispatcher.hpp"
#include "human_interface_device.hpp"
#include "logger.hpp"
#include "types.hpp"
#include <IOKit/hid/IOHIDManager.h>
#include <unordered_map>

namespace krbn {
class hid_manager final : public pqrs::dispatcher::extra::dispatcher_client {
public:
  // Signals (invoked from the shared dispatcher thread)

  // Return false to ignore device.
  boost::signals2::signal<bool(IOHIDDeviceRef _Nonnull), boost_utility::signals2_combiner_call_while_true> device_detecting;
  boost::signals2::signal<void(std::weak_ptr<human_interface_device>)> device_detected;
  boost::signals2::signal<void(std::weak_ptr<human_interface_device>)> device_removed;

  // Methods

  hid_manager(const hid_manager&) = delete;

  hid_manager(const std::vector<std::pair<hid_usage_page, hid_usage>>& usage_pairs) : dispatcher_client(),
                                                                                usage_pairs_(usage_pairs),
                                                                                manager_(nullptr),
                                                                                refresh_timer_(*this) {
    run_loop_thread_ = std::make_unique<cf_utility::run_loop_thread>();
  }

  virtual ~hid_manager(void) {
    detach_from_dispatcher([this] {
      stop();
    });

    run_loop_thread_->terminate();
    run_loop_thread_ = nullptr;
  }

  void async_start(void) {
    enqueue_to_dispatcher([this] {
      start();
    });
  }

  void async_stop(void) {
    enqueue_to_dispatcher([this] {
      stop();
    });
  }

  std::vector<std::shared_ptr<human_interface_device>> copy_hids(void) const {
    std::lock_guard<std::mutex> lock(hids_mutex_);

    return hids_;
  }

private:
  // This method is executed in the dispatcher thread.
  void start(void) {
    if (manager_) {
      logger::get_logger().warn("hid_manager is already started.");
      return;
    }

    manager_ = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
    if (!manager_) {
      logger::get_logger().error("IOHIDManagerCreate is failed.");
      return;
    }

    if (auto device_matching_dictionaries = iokit_utility::create_device_matching_dictionaries(usage_pairs_)) {
      IOHIDManagerSetDeviceMatchingMultiple(manager_, device_matching_dictionaries);
      CFRelease(device_matching_dictionaries);
    }

    IOHIDManagerRegisterDeviceMatchingCallback(manager_, static_device_matching_callback, this);
    IOHIDManagerRegisterDeviceRemovalCallback(manager_, static_device_removal_callback, this);

    IOHIDManagerScheduleWithRunLoop(manager_,
                                    run_loop_thread_->get_run_loop(),
                                    kCFRunLoopCommonModes);

    IOHIDManagerOpen(manager_, kIOHIDOptionsTypeNone);

    // We need to call `wake` after `IOHIDManagerScheduleWithRunLoop`.

    run_loop_thread_->wake();

    refresh_timer_.start(
        [this] {
          refresh_if_needed();
        },
        std::chrono::milliseconds(5000));

    logger::get_logger().info("hid_manager is started.");
  }

  // This method is executed in the dispatcher thread.
  void stop(void) {
    if (!manager_) {
      return;
    }

    // refresh_timer_

    refresh_timer_.stop();

    // manager_

    run_loop_thread_->enqueue_and_wait(^{
      // Release manager_ in run_loop_thread_ to avoid accessing released manager_ in run_loop_thread_.

      IOHIDManagerClose(manager_, kIOHIDOptionsTypeNone);
      IOHIDManagerUnscheduleFromRunLoop(manager_,
                                        run_loop_thread_->get_run_loop(),
                                        kCFRunLoopCommonModes);

      CFRelease(manager_);
      manager_ = nullptr;
    });

    // Other variables

    registry_entry_ids_.clear();

    {
      std::lock_guard<std::mutex> lock(hids_mutex_);

      hids_.clear();
    }

    logger::get_logger().info("hid_manager is stopped.");
  }

  // This method is executed in the dispatcher thread.
  void refresh_if_needed(void) {
    // `device_removal_callback` is sometimes missed
    // and invalid human_interface_devices are remained into hids_.
    // (The problem is likely occur just after wake up from sleep.)
    //
    // We validate the human_interface_device,
    // and then reload devices if there is an invalid human_interface_device.

    bool needs_refresh = false;

    {
      std::lock_guard<std::mutex> lock(hids_mutex_);

      for (const auto& hid : hids_) {
        if (!hid->validate()) {
          logger::get_logger().warn("Refreshing hid_manager since a dangling human_interface_device is found. ({0})",
                                    hid->get_name_for_log());
          needs_refresh = true;
          break;
        }
      }
    }

    if (needs_refresh) {
      stop();
      start();
    }
  }

  boost::optional<registry_entry_id> find_registry_entry_id(IOHIDDeviceRef _Nonnull device) {
    auto it = registry_entry_ids_.find(device);
    if (it != std::end(registry_entry_ids_)) {
      return it->second;
    }
    return boost::none;
  }

  static void static_device_matching_callback(void* _Nullable context,
                                              IOReturn result,
                                              void* _Nullable sender,
                                              IOHIDDeviceRef _Nonnull device) {
    if (result != kIOReturnSuccess) {
      return;
    }

    auto self = static_cast<hid_manager*>(context);
    if (!self) {
      return;
    }

    self->device_matching_callback(device);
  }

  void device_matching_callback(IOHIDDeviceRef _Nonnull device) {
    // `static_device_matching_callback` is called by multiple threads.
    // (It is not called from `run_loop_thread_`.)
    // Thus, we have to synchronize by using `dispatcher`.

    if (!device) {
      return;
    }

    CFRetain(device);

    enqueue_to_dispatcher([this, device] {
      if (!device_detecting(device)) {
        goto finish;
      }

      if (auto registry_entry_id = iokit_utility::find_registry_entry_id(device)) {
        // iokit_utility::find_registry_entry_id will be failed in device_removal_callback at least on macOS 10.13.
        // Thus, we have to memory device and registry_entry_id correspondence by myself.
        registry_entry_ids_[device] = *registry_entry_id;

        // Skip if same device is already matched.
        // (Multiple usage device (e.g. usage::pointer and usage::mouse) will be matched twice.)

        std::shared_ptr<human_interface_device> hid;

        {
          std::lock_guard<std::mutex> lock(hids_mutex_);

          if (std::any_of(std::begin(hids_),
                          std::end(hids_),
                          [&](auto&& h) {
                            return *registry_entry_id == h->get_registry_entry_id();
                          })) {
            // logger::get_logger().info("registry_entry_id:{0} already exists", static_cast<uint64_t>(*registry_entry_id));
            goto finish;
          }

          hid = std::make_shared<human_interface_device>(device, *registry_entry_id);
          hids_.push_back(hid);
        }

        enqueue_to_dispatcher([this, hid] {
          device_detected(hid);
        });
      }

    finish:
      CFRelease(device);
    });
  }

  static void static_device_removal_callback(void* _Nullable context,
                                             IOReturn result,
                                             void* _Nullable sender,
                                             IOHIDDeviceRef _Nonnull device) {
    if (result != kIOReturnSuccess) {
      return;
    }

    auto self = static_cast<hid_manager*>(context);
    if (!self) {
      return;
    }

    self->device_removal_callback(device);
  }

  void device_removal_callback(IOHIDDeviceRef _Nonnull device) {
    // `static_device_removal_callback` is called by multiple threads.
    // (It is not called from `run_loop_thread_`.)
    // Thus, we have to synchronize by using `dispatcher`.

    if (!device) {
      return;
    }

    CFRetain(device);

    enqueue_to_dispatcher([this, device] {
      if (auto registry_entry_id = find_registry_entry_id(device)) {
        std::shared_ptr<human_interface_device> hid;

        {
          std::lock_guard<std::mutex> lock(hids_mutex_);

          auto it = std::find_if(std::begin(hids_),
                                 std::end(hids_),
                                 [&](auto&& h) {
                                   return *registry_entry_id == h->get_registry_entry_id();
                                 });
          if (it != hids_.end()) {
            hid = *it;
            hids_.erase(it);
          }
        }

        if (hid) {
          hid->set_removed();

          enqueue_to_dispatcher([this, hid] {
            device_removed(hid);
          });
        }

        // There might be multiple devices for one registry_entry_id.
        // (For example, keyboard and mouse combined device.)
        // And there is possibility that removal callback is not called with some device.
        //
        // For example, there are 3 devices for 1 registry_entry_id (4294974284).
        //   - device1 { device:0x7fb9d64078a0 => registry_entry_id:4294974284 }
        //   - device2 { device:0x7fb9d8301390 => registry_entry_id:4294974284 }
        //   - device3 { device:0x7fb9d830a630 => registry_entry_id:4294974284 }
        // And device_removal_callback are called only with device1 and device2.
        //
        // We should remove device1, device2 and device3 at the same time in order to deal with this case.

        for (auto it = std::begin(registry_entry_ids_); it != std::end(registry_entry_ids_);) {
          if (it->second == *registry_entry_id) {
            it = registry_entry_ids_.erase(it);
          } else {
            std::advance(it, 1);
          }
        }
      }

      CFRelease(device);
    });
  }

  std::unique_ptr<cf_utility::run_loop_thread> run_loop_thread_;

  std::vector<std::pair<hid_usage_page, hid_usage>> usage_pairs_;

  IOHIDManagerRef _Nullable manager_;
  pqrs::dispatcher::extra::timer refresh_timer_;

  std::unordered_map<IOHIDDeviceRef, registry_entry_id> registry_entry_ids_;

  std::vector<std::shared_ptr<human_interface_device>> hids_;
  mutable std::mutex hids_mutex_;
};
} // namespace krbn
<|endoftext|>


<commit_before>#include "physicsEntity.h"

//
// CONSTRUCTORS ////////////////////////////////////////////////////////////////
//

PhysicsEntity::PhysicsEntity( QString pathToModel, QString pathToTexture,
    btScalar mass )
    :   m_pathToModel( pathToModel ), m_pathToTexture( pathToTexture ),
        m_mass( mass )
{
    ModelLoader::loadModel( pathToModel, m_model, m_numVertices );
    ModelLoader::loadTriMesh( pathToModel, m_triMesh );

    // Initialize Bullet
    m_collisionShape = new btBvhTriangleMeshShape( m_triMesh, true );

    m_motionState = new btDefaultMotionState( btTransform(
    btQuaternion( 0, 0, 0, 1 ), btVector3( 0, 0, 0 ) ) );
    // try changing one of the vectors
    Inertia = btVector3( 0, 0, 0 );
    // m_mass??
    m_collisionShape->calculateLocalInertia( m_mass, Inertia );
    m_rigidBodyCI = new btRigidBody::btRigidBodyConstructionInfo(
        m_mass, m_motionState, m_collisionShape, Inertia );

    RigidBody = new btRigidBody( *m_rigidBodyCI );
}

PhysicsEntity::~PhysicsEntity()
{
    teardownGL();

    delete m_triMesh;
    delete m_collisionShape;
    delete m_motionState;
    delete m_rigidBodyCI;
    delete RigidBody;
}

//
// RENDERABLE FUNCTINOS ////////////////////////////////////////////////////////
//

void PhysicsEntity::initializeGL()
{
    initializeOpenGLFunctions();

    // Create the shader this entity will use
    m_program = new QOpenGLShaderProgram();
    m_program->addShaderFromSourceFile( QOpenGLShader::Vertex,
        PATH_TO_V_SHADER );
    m_program->addShaderFromSourceFile( QOpenGLShader::Fragment,
        PATH_TO_F_SHADER );
    m_program->link();
    m_program->bind();

    // Cache the Uniform Locations
    m_modelWorld = m_program->uniformLocation( "model_to_world" );
    m_worldEye = m_program->uniformLocation( "world_to_eye" );
    m_eyeClip = m_program->uniformLocation( "eye_to_clip" );

    // Create the Texture Buffer Object
    m_texture = new QOpenGLTexture( QImage( m_pathToTexture ).mirrored() );
    m_texture->setMinificationFilter( QOpenGLTexture::LinearMipMapLinear );
    m_texture->setMagnificationFilter( QOpenGLTexture::Linear );

    // Create the Vertex Buffer Object
    m_vbo = new QOpenGLBuffer();
    m_vbo->create();
    m_vbo->bind();
    m_vbo->setUsagePattern( QOpenGLBuffer::StaticDraw );
    m_vbo->allocate( m_model, m_numVertices * sizeof( m_model[0] ) );

    // Create the Vertex Array Object
    m_vao = new QOpenGLVertexArrayObject();
    m_vao->create();
    m_vao->bind();
    m_program->enableAttributeArray( 0 );
    m_program->enableAttributeArray( 1 );
    m_program->setAttributeBuffer(  0,
                                    GL_FLOAT,
                                    Vertex::positionOffset(),
                                    Vertex::PositionTupleSize,
                                    Vertex::stride() );
    m_program->setAttributeBuffer(  1,
                                    GL_FLOAT,
                                    Vertex::uvOffset(),
                                    Vertex::UVTupleSize,
                                    Vertex::stride() );

    // Release all in order
    m_vao->release();
    m_vbo->release();
    m_texture->release();
    m_program->release();
}

void PhysicsEntity::paintGL( Camera3D& camera, QMatrix4x4& projection )
{
    m_program->bind();

    m_program->setUniformValue( m_worldEye, camera.toMatrix() );
    m_program->setUniformValue( m_eyeClip, projection );

    m_vao->bind();
    m_texture->bind();

    m_program->setUniformValue( m_modelWorld, Transform );

    glDrawArrays( GL_TRIANGLES, 0, m_numVertices );

    m_texture->release();
    m_vao->release();
    m_program->release();
}

void PhysicsEntity::update()
{
}

void PhysicsEntity::teardownGL()
{
    delete m_vbo;
    delete m_vao;
    delete m_program;
    delete m_model;
    delete m_texture;
}<commit_msg>nevermind about m_mass<commit_after>#include "physicsEntity.h"

//
// CONSTRUCTORS ////////////////////////////////////////////////////////////////
//

PhysicsEntity::PhysicsEntity( QString pathToModel, QString pathToTexture,
    btScalar mass )
    :   m_pathToModel( pathToModel ), m_pathToTexture( pathToTexture ),
        m_mass( mass )
{
    ModelLoader::loadModel( pathToModel, m_model, m_numVertices );
    ModelLoader::loadTriMesh( pathToModel, m_triMesh );

    // Initialize Bullet
    m_collisionShape = new btBvhTriangleMeshShape( m_triMesh, true );

    m_motionState = new btDefaultMotionState( btTransform(
    btQuaternion( 0, 0, 0, 1 ), btVector3( 0, 0, 0 ) ) );
    // try changing one of the vectors
    Inertia = btVector3( 0, 0, 0 );

    m_collisionShape->calculateLocalInertia( m_mass, Inertia );
    m_rigidBodyCI = new btRigidBody::btRigidBodyConstructionInfo(
        m_mass, m_motionState, m_collisionShape, Inertia );

    RigidBody = new btRigidBody( *m_rigidBodyCI );
}

PhysicsEntity::~PhysicsEntity()
{
    teardownGL();

    delete m_triMesh;
    delete m_collisionShape;
    delete m_motionState;
    delete m_rigidBodyCI;
    delete RigidBody;
}

//
// RENDERABLE FUNCTINOS ////////////////////////////////////////////////////////
//

void PhysicsEntity::initializeGL()
{
    initializeOpenGLFunctions();

    // Create the shader this entity will use
    m_program = new QOpenGLShaderProgram();
    m_program->addShaderFromSourceFile( QOpenGLShader::Vertex,
        PATH_TO_V_SHADER );
    m_program->addShaderFromSourceFile( QOpenGLShader::Fragment,
        PATH_TO_F_SHADER );
    m_program->link();
    m_program->bind();

    // Cache the Uniform Locations
    m_modelWorld = m_program->uniformLocation( "model_to_world" );
    m_worldEye = m_program->uniformLocation( "world_to_eye" );
    m_eyeClip = m_program->uniformLocation( "eye_to_clip" );

    // Create the Texture Buffer Object
    m_texture = new QOpenGLTexture( QImage( m_pathToTexture ).mirrored() );
    m_texture->setMinificationFilter( QOpenGLTexture::LinearMipMapLinear );
    m_texture->setMagnificationFilter( QOpenGLTexture::Linear );

    // Create the Vertex Buffer Object
    m_vbo = new QOpenGLBuffer();
    m_vbo->create();
    m_vbo->bind();
    m_vbo->setUsagePattern( QOpenGLBuffer::StaticDraw );
    m_vbo->allocate( m_model, m_numVertices * sizeof( m_model[0] ) );

    // Create the Vertex Array Object
    m_vao = new QOpenGLVertexArrayObject();
    m_vao->create();
    m_vao->bind();
    m_program->enableAttributeArray( 0 );
    m_program->enableAttributeArray( 1 );
    m_program->setAttributeBuffer(  0,
                                    GL_FLOAT,
                                    Vertex::positionOffset(),
                                    Vertex::PositionTupleSize,
                                    Vertex::stride() );
    m_program->setAttributeBuffer(  1,
                                    GL_FLOAT,
                                    Vertex::uvOffset(),
                                    Vertex::UVTupleSize,
                                    Vertex::stride() );

    // Release all in order
    m_vao->release();
    m_vbo->release();
    m_texture->release();
    m_program->release();
}

void PhysicsEntity::paintGL( Camera3D& camera, QMatrix4x4& projection )
{
    m_program->bind();

    m_program->setUniformValue( m_worldEye, camera.toMatrix() );
    m_program->setUniformValue( m_eyeClip, projection );

    m_vao->bind();
    m_texture->bind();

    m_program->setUniformValue( m_modelWorld, Transform );

    glDrawArrays( GL_TRIANGLES, 0, m_numVertices );

    m_texture->release();
    m_vao->release();
    m_program->release();
}

void PhysicsEntity::update()
{
}

void PhysicsEntity::teardownGL()
{
    delete m_vbo;
    delete m_vao;
    delete m_program;
    delete m_model;
    delete m_texture;
}<|endoftext|>


<commit_before>/*******************************************************************
* simulation.cpp
* Programming Assignment 1 : Sensor Simulation
* Author: Samuel Sikes
* Date: June 2016
* This program is entirely my own work
*******************************************************************/
#include "simulation.h"
/********************************/
/********************************/
/*********PRIVATE METHODS********/
/********************************/
/***********************************/
void simulation::simulationDescription(){

    for (int i = 0; i < 60; i++)
        std::cout << "=";

    std::cout <<  "\n" << "|" << std::setw(52) <<
    "ENVIRONMENTAL SENSORS SIMULATION DEMONSTRATION" <<
    std::setw(7) << "|" << "\n" <<
    "|" << std::setw(46) <<
    "CS 307 -- Programming Assignment 1" <<
    std::setw(13) << "|" << "\n";

    for (int i = 0; i < 60; i++)
        std::cout << "=";
    std::cout << "\n\n";
};
/***********************************/
void simulation::getFile(){

    char fileName[64];
    std::cout << "Please enter the name of the simulation data file then press Enter: ";
    std::cin >> fileName;
    dataParserPtr = new EnviroSimDataParser(fileName);
};
/***********************************/
void simulation::buildDevices(){

    int sensorCount = dataParserPtr->getSensorCount();
    int displayCount = dataParserPtr->getDisplayCount();
    createDisplays(displayCount);
    createSensors(sensorCount);

    delete dataParserPtr; dataParserPtr = NULL;
};
/***********************************/
void simulation::createDisplays(int& count){

    for(int i=0; i < count; i++){
        displayNode* displayNodePtr = new displayNode;
        if(!dataParserPtr->getDisplayData(displayNodePtr->type,displayNodePtr->IDs,&displayNodePtr->IDCount))
            cout << "\n" << "Parsing for Display " << i << " failed" << "\n";
        displayPtr->getDisplay(displayNodePtr);
    }
};
/***********************************/
void simulation::createSensors(int& count){

    for(int i=0; i < count; i++){
        sensorNode* sensorNodePtr = new sensorNode;
        if(!dataParserPtr->getSensorData(sensorNodePtr->type,sensorNodePtr->material,&sensorNodePtr->ID,
                                         sensorNodePtr->units,&sensorNodePtr->minVal,&sensorNodePtr->maxVal))
            cout << "\n" << "Parsing for Sensor " << i << " failed" << "\n";
        sensorNodePtr->sensorData = 0;
        sensorPtr->getSensor(sensorNodePtr);
    }
};
/***********************************/
void simulation::attachDevices(){

    sensorMountPtr->attachDisplays(displayPtr->relayDisplayData());
    sensorMountPtr->attachSensors(sensorPtr->relaySensorData());
    if(!sensorMountPtr->linkSensorsToDisplays())
        std::cout << "\n" << "Linking Sensors To Displays FAILED" << "\n";
};
/********************************/
/********************************/
/*********PUBLIC METHODS*********/
/********************************/
/***********************************/
simulation::simulation(){

    sensorPtr = new sensor;
    displayPtr = new display;
    sensorMountPtr = new sensorMount;
};
/***********************************/
simulation::~simulation(){

    delete sensorPtr; sensorPtr = NULL;
    delete displayPtr; displayPtr = NULL;
    delete sensorMountPtr; sensorMountPtr = NULL;
};
/***********************************/
void simulation::runSimulation() {

    simulationDescription();
    getFile();
    buildDevices();
    attachDevices();
    sensorMountPtr->displayConnectedDevices();
    /*- - - - - - - - - - - - -*/
    std::cout << "\n\n\n" << "Beginning simulation run..." << "\n\n";
    for (int i = 0; i < 20; i++)
        std::cout << "=";
    std::cout << " Running simulation ";
    for (int i = 0; i < 20; i++)
        std::cout << "=";
    std::cout << "\n\n" << "Press Ctrl-c to terminate the simulation" << "\n\n";
    /*- - - - - - - - - - - - -*/
    while(true){
        usleep(5000000);
        /*- - - - - - - - - - - - -*/
        for (int i = 0; i < 60; i++)
            std::cout << "=";
        std::cout << "\n\n";
        /*- - - - - - - - - - - - -*/
        sensorPtr->updateSensors();
        displayPtr->displayData();
        /*- - - - - - - - - - - - -*/
        std::cout << "\n\n\n";
        /*- - - - - - - - - - - - -*/
    }
};
<commit_msg>Update simulation.cpp<commit_after>//====================================================================
// simulation.cpp
// Programming Assignment 1 : Sensor Simulation
// Author: Samuel Sikes
// Date: June 2016
// This program is entirely my own work
//====================================================================
#include "simulation.h"

//---------------------------------
//---------------------------------
//--------PRIVATE METHODS----------
//---------------------------------
//---------------------------------

//---------------------------------
//function: simulationDescription()
//Displays a description of the
//program.
//---------------------------------
void simulation::simulationDescription(){

    for (int i = 0; i < 60; i++)
        std::cout << "=";

    std::cout <<  "\n" << "|" << std::setw(52) <<
    "ENVIRONMENTAL SENSORS SIMULATION DEMONSTRATION" <<
    std::setw(7) << "|" << "\n" <<
    "|" << std::setw(46) <<
    "CS 307 -- Programming Assignment 1" <<
    std::setw(13) << "|" << "\n";

    for (int i = 0; i < 60; i++)
        std::cout << "=";
    std::cout << "\n\n";
};

//---------------------------------
//function: getFile()
//Prompts user to enter the name
//of the file that they want parsed.
//---------------------------------
void simulation::getFile(){

    char fileName[64];
    std::cout << "Please enter the name of the simulation data file then press Enter: ";
    std::cin >> fileName;
    dataParserPtr = new EnviroSimDataParser(fileName);
};

//---------------------------------
//function: buildDevices()
//Gets the number of sensor and
//display devices to create and
//passes those counts to the create
//functions for both displays and
//sensors. Then, deletes instance of
//data parser.
//---------------------------------
void simulation::buildDevices(){

    int sensorCount = dataParserPtr->getSensorCount();
    int displayCount = dataParserPtr->getDisplayCount();
    this->createDisplays(displayCount);
    this->createSensors(sensorCount);

    delete dataParserPtr; dataParserPtr = NULL;
};

//---------------------------------
//function: createDisplays()
//Creates the appropriate number of
//displays and passes each display
//to display's getDisplay() method
//---------------------------------
void simulation::createDisplays(int& count){

    for(int i=0; i < count; i++){
        displayNode* displayNodePtr = new displayNode;
        if(!dataParserPtr->getDisplayData(displayNodePtr->type,displayNodePtr->IDs,&displayNodePtr->IDCount))
            cout << "\n" << "Parsing for Display " << i << " failed" << "\n";
        displayPtr->getDisplay(displayNodePtr);
    }
};

//---------------------------------
//function: createSensors()
//Creates the appropriate number of
//sensors and passes each sensor
//to sensor's getSensor() method
//---------------------------------
void simulation::createSensors(int& count){

    for(int i=0; i < count; i++){
        sensorNode* sensorNodePtr = new sensorNode;
        if(!dataParserPtr->getSensorData(sensorNodePtr->type,sensorNodePtr->material,&sensorNodePtr->ID,
                                         sensorNodePtr->units,&sensorNodePtr->minVal,&sensorNodePtr->maxVal))
            cout << "\n" << "Parsing for Sensor " << i << " failed" << "\n";
        sensorNodePtr->sensorData = 0;
        sensorPtr->getSensor(sensorNodePtr);
    }
};

//---------------------------------
//function: attachDevices()
//Attaches all sensors and display
//devices to sensor mount. Then,
//calls sensor mount funciton to
//link all sensors to the appropriate
//display devices.
//---------------------------------
void simulation::attachDevices(){

    sensorMountPtr->attachDisplays(displayPtr->relayDisplayData());
    sensorMountPtr->attachSensors(sensorPtr->relaySensorData());
    if(!sensorMountPtr->linkSensorsToDisplays())
        std::cout << "\n" << "Linking Sensors To Displays FAILED" << "\n";
};

//---------------------------------
//---------------------------------
//--------PUBLIC METHODS-----------
//---------------------------------
//---------------------------------

//---------------------------------
//function: simulation()
//dynamically creates instances of
//sensor, display, and sensor mount
//---------------------------------
simulation::simulation(){

    sensorPtr = new sensor;
    displayPtr = new display;
    sensorMountPtr = new sensorMount;
};

//---------------------------------
//function: ~simulation()
//deallocates memory for: display,
//sensor, and sensor mount
//sets all private pointers to NULL
//---------------------------------
simulation::~simulation(){

    delete sensorPtr; sensorPtr = NULL;
    delete displayPtr; displayPtr = NULL;
    delete sensorMountPtr; sensorMountPtr = NULL;
};

//---------------------------------
//function: runSimulation()
//calls appropriate simulation
//methods. Then, generates sensor
//data and displays it every 5
//seconds
//---------------------------------
void simulation::runSimulation() {

    this->simulationDescription();
    this->getFile();
    this->buildDevices();
    this->attachDevices();
    sensorMountPtr->displayConnectedDevices();
    /*- - - - - - - - - - - - -*/
    std::cout << "\n\n\n" << "Beginning simulation run..." << "\n\n";
    for (int i = 0; i < 20; i++)
        std::cout << "=";
    std::cout << " Running simulation ";
    for (int i = 0; i < 20; i++)
        std::cout << "=";
    std::cout << "\n\n" << "Press Ctrl-c to terminate the simulation" << "\n\n";
    /*- - - - - - - - - - - - -*/
    while(true){
        usleep(5000000);
        /*- - - - - - - - - - - - -*/
        for (int i = 0; i < 60; i++)
            std::cout << "=";
        std::cout << "\n\n";
        /*- - - - - - - - - - - - -*/
        sensorPtr->updateSensors();
        displayPtr->displayData();
        /*- - - - - - - - - - - - -*/
        std::cout << "\n\n\n";
        /*- - - - - - - - - - - - -*/
    }
};
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Monteverdi2
  Language:  C++

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See Copyright.txt for details.

  Monteverdi2 is distributed under the CeCILL licence version 2. See
  Licence_CeCILL_V2-en.txt or
  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt for more details.

  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "mvdGLImageWidget.h"

//
// Qt includes (sorted by alphabetic order)
//// Must be included before system/custom includes.

//
// System includes (sorted by alphabetic order)

//
// ITK includes (sorted by alphabetic order)
#include "itkImageRegionConstIteratorWithIndex.h"

//
// OTB includes (sorted by alphabetic order)

//
// Monteverdi includes (sorted by alphabetic order)
#include "mvdApplication.h"

namespace mvd
{
/*
  TRANSLATOR mvd::GLImageWidget

  Necessary for lupdate to be aware of C++ namespaces.

  Context comment for translator.
*/

/*******************************************************************************/
GLImageWidget
::GLImageWidget(QWidget *parent) :
  QGLWidget(parent),
  m_ImageViewManipulator( NULL ),
  m_ImageModelRenderer( NULL )
/*,  m_ImageToScreenTransform(AffineTransformType::New()),
  m_ScreenToImageTransform(AffineTransformType::New()) */
{
  m_ImageViewManipulator = new ImageViewManipulator(parent);
  m_ImageModelRenderer   = new ImageModelRendererType(parent);
}

/*******************************************************************************/
GLImageWidget
::~GLImageWidget()
{
}

/*******************************************************************************/
/*
void GLImageWidget::UpdateTransforms(int w, int h)
{

  if (m_IsotropicZoom <= 0)
    {
    itkExceptionMacro(<< "Internal error: Isotropic zoom should be non null positive.");
    }

  RegionType::IndexType index;
  RegionType::SizeType  size;
  // Update image extent
  std::cout << " size1 " << size[1] << std::endl;
  size[0] = static_cast<unsigned int>(m_IsotropicZoom * static_cast<double>(m_OpenGlBufferedRegion.GetSize()[0]));
  size[1] = static_cast<unsigned int>(m_IsotropicZoom * static_cast<double>(m_OpenGlBufferedRegion.GetSize()[1]));
  index[0] = m_OpenGlBufferedRegion.GetIndex()[0];
  index[1] = m_OpenGlBufferedRegion.GetIndex()[1];
  std::cout <<"GLWidget x "<< this->x() << " y "<< this->y() << std::endl;
  m_Extent.SetIndex(index) ;
  m_Extent.SetSize(size);

  std::cout <<"m_Extent : "<< m_Extent << std::endl;

  // Image to screen matrix
  AffineTransformType::MatrixType s2iMatrix;
  s2iMatrix.Fill(0);
  const double s2iSpacing = (m_SubsamplingRate) / m_IsotropicZoom;
  s2iMatrix(0, 0) = s2iSpacing;
  s2iMatrix(1, 1) = -s2iSpacing;
  m_ScreenToImageTransform->SetMatrix(s2iMatrix);

  // Image to screen translation
  AffineTransformType::OutputVectorType translation;
  translation[0] = m_SubsamplingRate *
    (m_OpenGlBufferedRegion.GetIndex()[0] -
     m_Extent.GetIndex()[0] / m_IsotropicZoom);
  translation[1] = m_SubsamplingRate *
    (((m_Extent.GetIndex()[1] +
       m_Extent.GetSize()[1]) / m_IsotropicZoom) + m_OpenGlBufferedRegion.GetIndex()[1]);
  m_ScreenToImageTransform->SetTranslation(translation) ;

  // Compute the inverse transform
  bool couldInvert = m_ScreenToImageTransform->GetInverse(m_ImageToScreenTransform);
  if (couldInvert == false)
    {
    itkExceptionMacro(<< "Internal error: Could not invert ScreenToImageTransform.");
    }
}
*/

/*******************************************************************************/
void
GLImageWidget
::initializeGL()
{
  glClearColor(0.0, 0.0, 0.0, 0.0);
  glShadeModel(GL_FLAT);
}

/*******************************************************************************/
void
GLImageWidget
::resizeGL(int width, int height)
{
  // TODO: Replace (GLint) casts with safer casts or no cast (if there is no compile-time warning).
  glViewport(0, 0, (GLint)width, (GLint)height);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, (GLint)width, 0, (GLint)height, -1, 1);
}

/*******************************************************************************/
void
GLImageWidget
::paintGL()
{
  // Clear back-buffer(s) before rendering sub-components.
  glClear( GL_COLOR_BUFFER_BIT );

  // Get the region to draw from the ImageViewManipulator navigation
  // context
  const ImageRegionType region(
    m_ImageViewManipulator->GetViewportImageRegion() );

  // Set the new rendering context to be known in the ModelRendere
  const AbstractImageModel* aiModel=  dynamic_cast< const AbstractImageModel* >(
    dynamic_cast< Application* >( qApp )->GetModel() );

  // setup the rendering context
  if (aiModel)
    {
    ImageModelRenderer::RenderingContext context(aiModel, region);

    // use the model renderer to paint the requested region of the image
    m_ImageModelRenderer->paintGL( context );
    }
}

/*******************************************************************************/
// Delegate the event to the ImageViewManipulator
void
GLImageWidget
::mousePressEvent(  QMouseEvent * event)
{
  m_ImageViewManipulator->mousePressEvent(event);
}

/*******************************************************************************/
void
GLImageWidget
::mouseMoveEvent(  QMouseEvent * event)
{
  m_ImageViewManipulator->mouseMoveEvent(event);
}

/*******************************************************************************/
void
GLImageWidget
::resizeEvent( QResizeEvent  * event)
{
  m_ImageViewManipulator->resizeEvent(event);
}

void GLImageWidget::onLargestPossibleRegionChanged(const ImageRegionType& largestRegion)
{
  m_ImageViewManipulator->SetImageLargestRegion(largestRegion);
}

/*******************************************************************************/
/* SLOTS                                                                       */
/*******************************************************************************/

/*******************************************************************************/

}
<commit_msg>ENH: repaint when dragging in the widget<commit_after>/*=========================================================================

  Program:   Monteverdi2
  Language:  C++

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See Copyright.txt for details.

  Monteverdi2 is distributed under the CeCILL licence version 2. See
  Licence_CeCILL_V2-en.txt or
  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt for more details.

  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "mvdGLImageWidget.h"

//
// Qt includes (sorted by alphabetic order)
//// Must be included before system/custom includes.

//
// System includes (sorted by alphabetic order)

//
// ITK includes (sorted by alphabetic order)
#include "itkImageRegionConstIteratorWithIndex.h"

//
// OTB includes (sorted by alphabetic order)

//
// Monteverdi includes (sorted by alphabetic order)
#include "mvdApplication.h"

namespace mvd
{
/*
  TRANSLATOR mvd::GLImageWidget

  Necessary for lupdate to be aware of C++ namespaces.

  Context comment for translator.
*/

/*******************************************************************************/
GLImageWidget
::GLImageWidget(QWidget *parent) :
  QGLWidget(parent),
  m_ImageViewManipulator( NULL ),
  m_ImageModelRenderer( NULL )
/*,  m_ImageToScreenTransform(AffineTransformType::New()),
  m_ScreenToImageTransform(AffineTransformType::New()) */
{
  m_ImageViewManipulator = new ImageViewManipulator(parent);
  m_ImageModelRenderer   = new ImageModelRendererType(parent);
}

/*******************************************************************************/
GLImageWidget
::~GLImageWidget()
{
}

/*******************************************************************************/
/*
void GLImageWidget::UpdateTransforms(int w, int h)
{

  if (m_IsotropicZoom <= 0)
    {
    itkExceptionMacro(<< "Internal error: Isotropic zoom should be non null positive.");
    }

  RegionType::IndexType index;
  RegionType::SizeType  size;
  // Update image extent
  std::cout << " size1 " << size[1] << std::endl;
  size[0] = static_cast<unsigned int>(m_IsotropicZoom * static_cast<double>(m_OpenGlBufferedRegion.GetSize()[0]));
  size[1] = static_cast<unsigned int>(m_IsotropicZoom * static_cast<double>(m_OpenGlBufferedRegion.GetSize()[1]));
  index[0] = m_OpenGlBufferedRegion.GetIndex()[0];
  index[1] = m_OpenGlBufferedRegion.GetIndex()[1];
  std::cout <<"GLWidget x "<< this->x() << " y "<< this->y() << std::endl;
  m_Extent.SetIndex(index) ;
  m_Extent.SetSize(size);

  std::cout <<"m_Extent : "<< m_Extent << std::endl;

  // Image to screen matrix
  AffineTransformType::MatrixType s2iMatrix;
  s2iMatrix.Fill(0);
  const double s2iSpacing = (m_SubsamplingRate) / m_IsotropicZoom;
  s2iMatrix(0, 0) = s2iSpacing;
  s2iMatrix(1, 1) = -s2iSpacing;
  m_ScreenToImageTransform->SetMatrix(s2iMatrix);

  // Image to screen translation
  AffineTransformType::OutputVectorType translation;
  translation[0] = m_SubsamplingRate *
    (m_OpenGlBufferedRegion.GetIndex()[0] -
     m_Extent.GetIndex()[0] / m_IsotropicZoom);
  translation[1] = m_SubsamplingRate *
    (((m_Extent.GetIndex()[1] +
       m_Extent.GetSize()[1]) / m_IsotropicZoom) + m_OpenGlBufferedRegion.GetIndex()[1]);
  m_ScreenToImageTransform->SetTranslation(translation) ;

  // Compute the inverse transform
  bool couldInvert = m_ScreenToImageTransform->GetInverse(m_ImageToScreenTransform);
  if (couldInvert == false)
    {
    itkExceptionMacro(<< "Internal error: Could not invert ScreenToImageTransform.");
    }
}
*/

/*******************************************************************************/
void
GLImageWidget
::initializeGL()
{
  glClearColor(0.0, 0.0, 0.0, 0.0);
  glShadeModel(GL_FLAT);
}

/*******************************************************************************/
void
GLImageWidget
::resizeGL(int width, int height)
{
  // TODO: Replace (GLint) casts with safer casts or no cast (if there is no compile-time warning).
  glViewport(0, 0, (GLint)width, (GLint)height);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, (GLint)width, 0, (GLint)height, -1, 1);
}

/*******************************************************************************/
void
GLImageWidget
::paintGL()
{
  // Clear back-buffer(s) before rendering sub-components.
  glClear( GL_COLOR_BUFFER_BIT );

  // Get the region to draw from the ImageViewManipulator navigation
  // context
  const ImageRegionType region(
    m_ImageViewManipulator->GetViewportImageRegion() );

  // Set the new rendering context to be known in the ModelRendere
  const AbstractImageModel* aiModel=  dynamic_cast< const AbstractImageModel* >(
    dynamic_cast< Application* >( qApp )->GetModel() );

  // setup the rendering context
  if (aiModel)
    {
    ImageModelRenderer::RenderingContext context(aiModel, region);

    // use the model renderer to paint the requested region of the image
    m_ImageModelRenderer->paintGL( context );
    }
}

/*******************************************************************************/
// Delegate the event to the ImageViewManipulator
void
GLImageWidget
::mousePressEvent(  QMouseEvent * event)
{
  m_ImageViewManipulator->mousePressEvent(event);
}

/*******************************************************************************/
void
GLImageWidget
::mouseMoveEvent(  QMouseEvent * event)
{
  m_ImageViewManipulator->mouseMoveEvent(event);

  // repaint the buffer
  this->update();
}

/*******************************************************************************/
void
GLImageWidget
::resizeEvent( QResizeEvent  * event)
{
  m_ImageViewManipulator->resizeEvent(event);
}

void GLImageWidget::onLargestPossibleRegionChanged(const ImageRegionType& largestRegion)
{
  m_ImageViewManipulator->SetImageLargestRegion(largestRegion);
}

/*******************************************************************************/
/* SLOTS                                                                       */
/*******************************************************************************/

/*******************************************************************************/

}
<|endoftext|>


<commit_before>/**
 * StimulusDisplay.cpp
 *
 * History:
 * Dave Cox on ??/??/?? - Created.
 * Paul Jankunas on 05/23/05 - Fixed spacing.
 *
 * Copyright 2005 MIT.  All rights reserved.
 */

#include "OpenGLContextManager.h"
#include "Stimulus.h"
#include "StimulusNode.h"
#include "Utilities.h"
#include "StandardVariables.h"
#include "Experiment.h"
#include "StandardVariables.h"
#include "ComponentRegistry.h"
#include "VariableNotification.h"

#include "boost/bind.hpp"

#ifdef	__APPLE__
	#include <AGL/agl.h>
	#include <OpenGL/gl.h>
	#include <OpenGL/glu.h>
    #include <CoreVideo/CVHostTime.h>
#elif	linux
	// TODO: where are these in linux?
#endif

using namespace mw;

/**********************************************************************
 *                  StimulusDisplay Methods
 **********************************************************************/
StimulusDisplay::StimulusDisplay() :
    refreshSync(2)
{
    current_context_index = -1;

    // defer creation of the display chain until after the stimulus display has been created
    display_stack = shared_ptr< LinkedList<StimulusNode> >(new LinkedList<StimulusNode>());

	setDisplayBounds();

    opengl_context_manager = OpenGLContextManager::instance();
    clock = Clock::instance();

    waitingForRefresh = false;
    needDraw = false;

    if (kCVReturnSuccess != CVDisplayLinkCreateWithActiveCGDisplays(&displayLink)) {
        throw SimpleException("Unable to create display link");
    }

    stateSystemNotification = shared_ptr<VariableCallbackNotification>(new VariableCallbackNotification(boost::bind(&StimulusDisplay::stateSystemCallback, this, _1, _2)));
    state_system_mode->addNotification(stateSystemNotification);
}

StimulusDisplay::~StimulusDisplay(){
    stateSystemNotification->remove();
    CVDisplayLinkRelease(displayLink);
}

void StimulusDisplay::setCurrent(int i){
    if ((i >= getNContexts()) || (i < 0)) {
        merror(M_DISPLAY_MESSAGE_DOMAIN, "invalid context index (%d)", i);
        return;
    }

	current_context_index = i;
	opengl_context_manager->setCurrent(context_ids[i]);
}

shared_ptr<StimulusNode> StimulusDisplay::addStimulus(shared_ptr<Stimulus> stim) {
    if(!stim) {
        mprintf("Attempt to load NULL stimulus");
        return shared_ptr<StimulusNode>();
    }

	shared_ptr<StimulusNode> stimnode(new StimulusNode(stim));

    display_stack->addToFront(stimnode);

	return stimnode;
}

void StimulusDisplay::addStimulusNode(shared_ptr<StimulusNode> stimnode) {
    if(!stimnode) {
        mprintf("Attempt to load NULL stimulus");
        return;
    }

	// remove it, in case it already belongs to a list
	stimnode->remove();

	display_stack->addToFront(stimnode);  // TODO
}

void StimulusDisplay::setDisplayBounds(){
  shared_ptr<mw::ComponentRegistry> reg = mw::ComponentRegistry::getSharedRegistry();
  shared_ptr<Variable> main_screen_info = reg->getVariable(MAIN_SCREEN_INFO_TAGNAME);

 Datum display_info = *main_screen_info; // from standard variables
	if(display_info.getDataType() == M_DICTIONARY &&
	   display_info.hasKey(M_DISPLAY_WIDTH_KEY) &&
	   display_info.hasKey(M_DISPLAY_HEIGHT_KEY) &&
	   display_info.hasKey(M_DISPLAY_DISTANCE_KEY)){

		GLdouble width_unknown_units = display_info.getElement(M_DISPLAY_WIDTH_KEY);
		GLdouble height_unknown_units = display_info.getElement(M_DISPLAY_HEIGHT_KEY);
		GLdouble distance_unknown_units = display_info.getElement(M_DISPLAY_DISTANCE_KEY);

		GLdouble half_width_deg = (180. / M_PI) * atan((width_unknown_units/2.)/distance_unknown_units);
		GLdouble half_height_deg = half_width_deg * height_unknown_units / width_unknown_units;
		//GLdouble half_height_deg = (180. / M_PI) * atan((height_unknown_units/2.)/distance_unknown_units);

		left = -half_width_deg;
		right = half_width_deg;
		top = half_height_deg;
		bottom = -half_height_deg;
	} else {
		left = M_STIMULUS_DISPLAY_LEFT_EDGE;
		right = M_STIMULUS_DISPLAY_RIGHT_EDGE;
		top = M_STIMULUS_DISPLAY_TOP_EDGE;
		bottom = M_STIMULUS_DISPLAY_BOTTOM_EDGE;
	}

	mprintf("Display bounds set to (%g left, %g right, %g top, %g bottom)",
			left, right, top, bottom);
}

void StimulusDisplay::getDisplayBounds(GLdouble &left, GLdouble &right, GLdouble &bottom, GLdouble &top) {
    left = this->left;
    right = this->right;
    bottom = this->bottom;
    top = this->top;
}

int StimulusDisplay::getMainDisplayRefreshRate() {
    int refreshRate = opengl_context_manager->getDisplayRefreshRate(opengl_context_manager->getMainDisplayIndex());
    if (refreshRate <= 0) {
        refreshRate = 60;
    }
    return refreshRate;
}

void StimulusDisplay::addContext(int _context_id){
	context_ids.push_back(_context_id);
}

void StimulusDisplay::getCurrentViewportSize(GLint &width, GLint &height) {
    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);
    width = viewport[2];
    height = viewport[3];
}

void StimulusDisplay::stateSystemCallback(const Datum &data, MWorksTime time) {
    unique_lock lock(display_lock);

    int newState = data.getInteger();

    if (IDLE == newState) {

        if (CVDisplayLinkIsRunning(displayLink)) {
            // If another thread is waiting for a display refresh, allow it to complete before stopping
            // the display link
            while (waitingForRefresh) {
                refreshCond.wait(lock);
            }

            // We need to use a shared_lock here and in StimulusDisplay::displayLinkCallback.
            // If we don't, displayLinkCallback could be blocked waiting for the lock, and
            // CVDisplayLinkStop won't return until displayLinkCallback exits, leading to deadlock.
            shared_lock sharedLock(lock);

            if (kCVReturnSuccess != CVDisplayLinkStop(displayLink)) {
                merror(M_DISPLAY_MESSAGE_DOMAIN, "Unable to stop display updates");
            } else {
                mprintf(M_DISPLAY_MESSAGE_DOMAIN, "Display updates stopped");
            }
        }

    } else if (RUNNING == newState) {

        if (!CVDisplayLinkIsRunning(displayLink)) {

            lastFrameTime = 0;

            if (kCVReturnSuccess != CVDisplayLinkSetOutputCallback(displayLink,
                                                                   &StimulusDisplay::displayLinkCallback,
                                                                   this) ||
                kCVReturnSuccess != opengl_context_manager->prepareDisplayLinkForMainDisplay(displayLink) ||
                kCVReturnSuccess != CVDisplayLinkStart(displayLink))
            {
                merror(M_DISPLAY_MESSAGE_DOMAIN, "Unable to start display updates");
            } else {
                mprintf(M_DISPLAY_MESSAGE_DOMAIN,
                        "Display updates started (main = %d, current = %d)",
                        CGMainDisplayID(),
                        CVDisplayLinkGetCurrentCGDisplay(displayLink));
            }

        }

    }
}

CVReturn StimulusDisplay::displayLinkCallback(CVDisplayLinkRef _displayLink,
                                              const CVTimeStamp *now,
                                              const CVTimeStamp *outputTime,
                                              CVOptionFlags flagsIn,
                                              CVOptionFlags *flagsOut,
                                              void *_display)
{
    StimulusDisplay *display = static_cast<StimulusDisplay*>(_display);

    {
        // See note on shared_lock in StimulusDisplay::stateSystemCallback
        shared_lock sharedLock(display->display_lock);

//#define WARN_ON_SKIPPED_REFRESH
#ifdef WARN_ON_SKIPPED_REFRESH
        if (display->lastFrameTime) {
            int64_t delta = (outputTime->videoTime - display->lastFrameTime) - outputTime->videoRefreshPeriod;
            if (delta) {
                mwarning(M_DISPLAY_MESSAGE_DOMAIN,
                         "Skipped %g display refresh cycles",
                         (double)delta / (double)(outputTime->videoRefreshPeriod));
            }
        }
#endif
        display->lastFrameTime = outputTime->videoTime;

        display->refreshDisplay();
        display->waitingForRefresh = false;
    }

    // Signal waiting threads that refresh is complete
    display->refreshCond.notify_all();

    return kCVReturnSuccess;
}

void StimulusDisplay::refreshDisplay() {
    //
    // Determine whether we need to draw
    //

    if (!needDraw) {
        shared_ptr<StimulusNode> node = display_stack->getFrontmost();
        while (node) {
            if (node->isVisible()) {
                needDraw = node->needDraw();
                if (needDraw)
                    break;
            }
            node = node->getNext();
        }
    }

    if (!needDraw) {
        return;
    }

    //
    // Draw stimuli
    //

    if (context_ids.size() > 0) {
        for (int i = 0; i < context_ids.size(); i++) {
            setCurrent(i);
            drawDisplayStack();

            if (i != 0) {

                // Non-main display
                opengl_context_manager->updateAndFlush(i);

            } else {

                // Main display

                opengl_context_manager->flush(i);
                if (opengl_context_manager->hasFence()) {
                    glSetFenceAPPLE(opengl_context_manager->getFence());
                }

                dispatch_async_f(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
                                 this,
                                 &StimulusDisplay::announceDisplayUpdate);

            }
        }

        // Wait for StimulusDisplay::announceDisplayUpdate to acquire the lock
        refreshSync.wait();
    }

    needDraw = false;
}

void StimulusDisplay::clearDisplay() {
    unique_lock lock(display_lock);

    shared_ptr<StimulusNode> node = display_stack->getFrontmost();
    while(node) {
        node->setVisible(false);
        node = node->getNext();
    }

    ensureRefresh(lock);
}

void StimulusDisplay::glInit() {

    glShadeModel(GL_FLAT);
    glDisable(GL_BLEND);
    glDisable(GL_DITHER);
    glDisable(GL_FOG);
    glDisable(GL_LIGHTING);

    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); // DDC added

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity(); // Reset The Projection Matrix

    gluOrtho2D(left, right, bottom, top);
    glMatrixMode(GL_MODELVIEW);

    glClearColor(0.5, 0.5, 0.5, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

}

void StimulusDisplay::drawDisplayStack() {
    // OpenGL setup

    glInit();

    // Draw all of the stimuli in the chain, back to front

    shared_ptr<StimulusNode> node = display_stack->getBackmost();
    while (node) {
        if (node->isVisible()) {
            node->draw(shared_from_this());
        }
        node = node->getPrevious();
    }
}

void StimulusDisplay::updateDisplay() {
	unique_lock lock(display_lock);

    shared_ptr<StimulusNode> node = display_stack->getFrontmost();
    while (node) {
        if (node->isPending()) {
            // we're taking care of the pending state, so
            // clear this flag
            node->clearPending();

            // convert "pending visible" stimuli to "visible" ones
            node->setVisible(node->isPendingVisible());

            if (node->isPendingRemoval()) {
                node->clearPendingRemoval();
                node->remove();
                continue;
            }
        }

        node = node->getNext();
    }

    ensureRefresh(lock);
}

void StimulusDisplay::ensureRefresh(unique_lock &lock) {
    shared_lock sharedLock(lock);

    MWTime before_draw = clock->getCurrentTimeUS();
    needDraw = true;

    if (!CVDisplayLinkIsRunning(displayLink)) {
        // Need to do the refresh here
        refreshDisplay();
    } else {
        // Wait for next display refresh to complete
        waitingForRefresh = true;
        do {
            refreshCond.wait(sharedLock);
        } while (waitingForRefresh);
    }

#define ERROR_ON_LATE_FRAMES
#ifdef ERROR_ON_LATE_FRAMES
    MWTime now = clock->getCurrentTimeUS();
    MWTime slop = 2 * (1000000 / getMainDisplayRefreshRate());

    if(now-before_draw > slop) {
        merror(M_DISPLAY_MESSAGE_DOMAIN,
               "updating main window display is taking longer than two frames (%lld > %lld) to update",
               now-before_draw,
               slop);
    }
#endif
}

void StimulusDisplay::announceDisplayUpdate(void *_display) {
    StimulusDisplay *display = static_cast<StimulusDisplay*>(_display);

    {
        shared_lock sharedLock(display->display_lock);

        // Wait for StimulusDisplay::refreshDisplay to finish drawing
        display->refreshSync.wait();

        display->setCurrent(0);
        if (display->opengl_context_manager->hasFence()) {
            glFinishFenceAPPLE(display->opengl_context_manager->getFence());
        }

        MWTime now = display->clock->getCurrentTimeUS();
        stimDisplayUpdate->setValue(display->getAnnounceData(), now);
        display->announceDisplayStack(now);
    }
}

void StimulusDisplay::announceDisplayStack(MWTime time) {
    shared_ptr<StimulusNode> node = display_stack->getBackmost(); //tail;

    while(node != shared_ptr< LinkedListNode<StimulusNode> >()) {
		if(node->isVisible()) {
            node->announceStimulusDraw(time);
		}

		node = node->getPrevious();
    }

}

Datum StimulusDisplay::getAnnounceData() {
    shared_ptr<StimulusNode> node = display_stack->getBackmost(); //tail;

    Datum stimAnnounce(M_LIST, 1);
    while(node != shared_ptr< LinkedListNode<StimulusNode> >()) {
		if(node->isVisible()) {
            Datum individualAnnounce(node->getCurrentAnnounceDrawData());
			if(!individualAnnounce.isUndefined()) {
				stimAnnounce.addElement(individualAnnounce);
			}
		}

		node = node->getPrevious();
    }
	return stimAnnounce;
}

shared_ptr<StimulusDisplay> StimulusDisplay::getCurrentStimulusDisplay() {
    if (!GlobalCurrentExperiment) {
        throw SimpleException("no experiment currently defined");
    }

    shared_ptr<StimulusDisplay> currentDisplay = GlobalCurrentExperiment->getStimulusDisplay();
    if (!currentDisplay) {
        throw SimpleException("no stimulus display in current experiment");
    }

    return currentDisplay;
}

<commit_msg>Ensure that display update announcement completes before next display update begins<commit_after>/**
 * StimulusDisplay.cpp
 *
 * History:
 * Dave Cox on ??/??/?? - Created.
 * Paul Jankunas on 05/23/05 - Fixed spacing.
 *
 * Copyright 2005 MIT.  All rights reserved.
 */

#include "OpenGLContextManager.h"
#include "Stimulus.h"
#include "StimulusNode.h"
#include "Utilities.h"
#include "StandardVariables.h"
#include "Experiment.h"
#include "StandardVariables.h"
#include "ComponentRegistry.h"
#include "VariableNotification.h"

#include "boost/bind.hpp"

#ifdef	__APPLE__
	#include <AGL/agl.h>
	#include <OpenGL/gl.h>
	#include <OpenGL/glu.h>
    #include <CoreVideo/CVHostTime.h>
#elif	linux
	// TODO: where are these in linux?
#endif

using namespace mw;

/**********************************************************************
 *                  StimulusDisplay Methods
 **********************************************************************/
StimulusDisplay::StimulusDisplay() :
    refreshSync(2)
{
    current_context_index = -1;

    // defer creation of the display chain until after the stimulus display has been created
    display_stack = shared_ptr< LinkedList<StimulusNode> >(new LinkedList<StimulusNode>());

	setDisplayBounds();

    opengl_context_manager = OpenGLContextManager::instance();
    clock = Clock::instance();

    waitingForRefresh = false;
    needDraw = false;

    if (kCVReturnSuccess != CVDisplayLinkCreateWithActiveCGDisplays(&displayLink)) {
        throw SimpleException("Unable to create display link");
    }

    stateSystemNotification = shared_ptr<VariableCallbackNotification>(new VariableCallbackNotification(boost::bind(&StimulusDisplay::stateSystemCallback, this, _1, _2)));
    state_system_mode->addNotification(stateSystemNotification);
}

StimulusDisplay::~StimulusDisplay(){
    stateSystemNotification->remove();
    CVDisplayLinkRelease(displayLink);
}

void StimulusDisplay::setCurrent(int i){
    if ((i >= getNContexts()) || (i < 0)) {
        merror(M_DISPLAY_MESSAGE_DOMAIN, "invalid context index (%d)", i);
        return;
    }

	current_context_index = i;
	opengl_context_manager->setCurrent(context_ids[i]);
}

shared_ptr<StimulusNode> StimulusDisplay::addStimulus(shared_ptr<Stimulus> stim) {
    if(!stim) {
        mprintf("Attempt to load NULL stimulus");
        return shared_ptr<StimulusNode>();
    }

	shared_ptr<StimulusNode> stimnode(new StimulusNode(stim));

    display_stack->addToFront(stimnode);

	return stimnode;
}

void StimulusDisplay::addStimulusNode(shared_ptr<StimulusNode> stimnode) {
    if(!stimnode) {
        mprintf("Attempt to load NULL stimulus");
        return;
    }

	// remove it, in case it already belongs to a list
	stimnode->remove();

	display_stack->addToFront(stimnode);  // TODO
}

void StimulusDisplay::setDisplayBounds(){
  shared_ptr<mw::ComponentRegistry> reg = mw::ComponentRegistry::getSharedRegistry();
  shared_ptr<Variable> main_screen_info = reg->getVariable(MAIN_SCREEN_INFO_TAGNAME);

 Datum display_info = *main_screen_info; // from standard variables
	if(display_info.getDataType() == M_DICTIONARY &&
	   display_info.hasKey(M_DISPLAY_WIDTH_KEY) &&
	   display_info.hasKey(M_DISPLAY_HEIGHT_KEY) &&
	   display_info.hasKey(M_DISPLAY_DISTANCE_KEY)){

		GLdouble width_unknown_units = display_info.getElement(M_DISPLAY_WIDTH_KEY);
		GLdouble height_unknown_units = display_info.getElement(M_DISPLAY_HEIGHT_KEY);
		GLdouble distance_unknown_units = display_info.getElement(M_DISPLAY_DISTANCE_KEY);

		GLdouble half_width_deg = (180. / M_PI) * atan((width_unknown_units/2.)/distance_unknown_units);
		GLdouble half_height_deg = half_width_deg * height_unknown_units / width_unknown_units;
		//GLdouble half_height_deg = (180. / M_PI) * atan((height_unknown_units/2.)/distance_unknown_units);

		left = -half_width_deg;
		right = half_width_deg;
		top = half_height_deg;
		bottom = -half_height_deg;
	} else {
		left = M_STIMULUS_DISPLAY_LEFT_EDGE;
		right = M_STIMULUS_DISPLAY_RIGHT_EDGE;
		top = M_STIMULUS_DISPLAY_TOP_EDGE;
		bottom = M_STIMULUS_DISPLAY_BOTTOM_EDGE;
	}

	mprintf("Display bounds set to (%g left, %g right, %g top, %g bottom)",
			left, right, top, bottom);
}

void StimulusDisplay::getDisplayBounds(GLdouble &left, GLdouble &right, GLdouble &bottom, GLdouble &top) {
    left = this->left;
    right = this->right;
    bottom = this->bottom;
    top = this->top;
}

int StimulusDisplay::getMainDisplayRefreshRate() {
    int refreshRate = opengl_context_manager->getDisplayRefreshRate(opengl_context_manager->getMainDisplayIndex());
    if (refreshRate <= 0) {
        refreshRate = 60;
    }
    return refreshRate;
}

void StimulusDisplay::addContext(int _context_id){
	context_ids.push_back(_context_id);
}

void StimulusDisplay::getCurrentViewportSize(GLint &width, GLint &height) {
    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);
    width = viewport[2];
    height = viewport[3];
}

void StimulusDisplay::stateSystemCallback(const Datum &data, MWorksTime time) {
    unique_lock lock(display_lock);

    int newState = data.getInteger();

    if (IDLE == newState) {

        if (CVDisplayLinkIsRunning(displayLink)) {
            // If another thread is waiting for a display refresh, allow it to complete before stopping
            // the display link
            while (waitingForRefresh) {
                refreshCond.wait(lock);
            }

            // We need to release the lock before calling CVDisplayLinkStop, because
            // StimulusDisplay::displayLinkCallback could be blocked waiting for the lock, and
            // CVDisplayLinkStop won't return until displayLinkCallback exits, leading to deadlock.
            lock.unlock();

            if (kCVReturnSuccess != CVDisplayLinkStop(displayLink)) {
                merror(M_DISPLAY_MESSAGE_DOMAIN, "Unable to stop display updates");
            } else {
                mprintf(M_DISPLAY_MESSAGE_DOMAIN, "Display updates stopped");
            }
        }

    } else if (RUNNING == newState) {

        if (!CVDisplayLinkIsRunning(displayLink)) {

            lastFrameTime = 0;

            if (kCVReturnSuccess != CVDisplayLinkSetOutputCallback(displayLink,
                                                                   &StimulusDisplay::displayLinkCallback,
                                                                   this) ||
                kCVReturnSuccess != opengl_context_manager->prepareDisplayLinkForMainDisplay(displayLink) ||
                kCVReturnSuccess != CVDisplayLinkStart(displayLink))
            {
                merror(M_DISPLAY_MESSAGE_DOMAIN, "Unable to start display updates");
            } else {
                mprintf(M_DISPLAY_MESSAGE_DOMAIN,
                        "Display updates started (main = %d, current = %d)",
                        CGMainDisplayID(),
                        CVDisplayLinkGetCurrentCGDisplay(displayLink));
            }

        }

    }
}

CVReturn StimulusDisplay::displayLinkCallback(CVDisplayLinkRef _displayLink,
                                              const CVTimeStamp *now,
                                              const CVTimeStamp *outputTime,
                                              CVOptionFlags flagsIn,
                                              CVOptionFlags *flagsOut,
                                              void *_display)
{
    StimulusDisplay *display = static_cast<StimulusDisplay*>(_display);

    {
        unique_lock lock(display->display_lock);

//#define WARN_ON_SKIPPED_REFRESH
#ifdef WARN_ON_SKIPPED_REFRESH
        if (display->lastFrameTime) {
            int64_t delta = (outputTime->videoTime - display->lastFrameTime) - outputTime->videoRefreshPeriod;
            if (delta) {
                mwarning(M_DISPLAY_MESSAGE_DOMAIN,
                         "Skipped %g display refresh cycles",
                         (double)delta / (double)(outputTime->videoRefreshPeriod));
            }
        }
#endif
        display->lastFrameTime = outputTime->videoTime;

        shared_lock sharedLock(lock);  // Downgrade to shared_lock
        display->refreshDisplay();
        display->waitingForRefresh = false;
    }

    // Signal waiting threads that refresh is complete
    display->refreshCond.notify_all();

    return kCVReturnSuccess;
}

void StimulusDisplay::refreshDisplay() {
    //
    // Determine whether we need to draw
    //

    if (!needDraw) {
        shared_ptr<StimulusNode> node = display_stack->getFrontmost();
        while (node) {
            if (node->isVisible()) {
                needDraw = node->needDraw();
                if (needDraw)
                    break;
            }
            node = node->getNext();
        }
    }

    if (!needDraw) {
        return;
    }

    //
    // Draw stimuli
    //

    if (context_ids.size() > 0) {
        for (int i = 0; i < context_ids.size(); i++) {
            setCurrent(i);
            drawDisplayStack();

            if (i != 0) {

                // Non-main display
                opengl_context_manager->updateAndFlush(i);

            } else {

                // Main display

                opengl_context_manager->flush(i);
                if (opengl_context_manager->hasFence()) {
                    glSetFenceAPPLE(opengl_context_manager->getFence());
                }

                dispatch_async_f(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
                                 this,
                                 &StimulusDisplay::announceDisplayUpdate);

            }
        }

        // Wait for StimulusDisplay::announceDisplayUpdate to acquire the lock
        refreshSync.wait();
    }

    needDraw = false;
}

void StimulusDisplay::clearDisplay() {
    unique_lock lock(display_lock);

    shared_ptr<StimulusNode> node = display_stack->getFrontmost();
    while(node) {
        node->setVisible(false);
        node = node->getNext();
    }

    ensureRefresh(lock);
}

void StimulusDisplay::glInit() {

    glShadeModel(GL_FLAT);
    glDisable(GL_BLEND);
    glDisable(GL_DITHER);
    glDisable(GL_FOG);
    glDisable(GL_LIGHTING);

    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); // DDC added

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity(); // Reset The Projection Matrix

    gluOrtho2D(left, right, bottom, top);
    glMatrixMode(GL_MODELVIEW);

    glClearColor(0.5, 0.5, 0.5, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

}

void StimulusDisplay::drawDisplayStack() {
    // OpenGL setup

    glInit();

    // Draw all of the stimuli in the chain, back to front

    shared_ptr<StimulusNode> node = display_stack->getBackmost();
    while (node) {
        if (node->isVisible()) {
            node->draw(shared_from_this());
        }
        node = node->getPrevious();
    }
}

void StimulusDisplay::updateDisplay() {
	unique_lock lock(display_lock);

    shared_ptr<StimulusNode> node = display_stack->getFrontmost();
    while (node) {
        if (node->isPending()) {
            // we're taking care of the pending state, so
            // clear this flag
            node->clearPending();

            // convert "pending visible" stimuli to "visible" ones
            node->setVisible(node->isPendingVisible());

            if (node->isPendingRemoval()) {
                node->clearPendingRemoval();
                node->remove();
                continue;
            }
        }

        node = node->getNext();
    }

    ensureRefresh(lock);
}

void StimulusDisplay::ensureRefresh(unique_lock &lock) {
    shared_lock sharedLock(lock);  // Downgrade to shared_lock

    MWTime before_draw = clock->getCurrentTimeUS();
    needDraw = true;

    if (!CVDisplayLinkIsRunning(displayLink)) {
        // Need to do the refresh here
        refreshDisplay();
    } else {
        // Wait for next display refresh to complete
        waitingForRefresh = true;
        do {
            refreshCond.wait(sharedLock);
        } while (waitingForRefresh);
    }

#define ERROR_ON_LATE_FRAMES
#ifdef ERROR_ON_LATE_FRAMES
    MWTime now = clock->getCurrentTimeUS();
    MWTime slop = 2 * (1000000 / getMainDisplayRefreshRate());

    if(now-before_draw > slop) {
        merror(M_DISPLAY_MESSAGE_DOMAIN,
               "updating main window display is taking longer than two frames (%lld > %lld) to update",
               now-before_draw,
               slop);
    }
#endif
}

void StimulusDisplay::announceDisplayUpdate(void *_display) {
    StimulusDisplay *display = static_cast<StimulusDisplay*>(_display);

    {
        shared_lock sharedLock(display->display_lock);

        // Wait for StimulusDisplay::refreshDisplay to finish drawing
        display->refreshSync.wait();

        display->setCurrent(0);
        if (display->opengl_context_manager->hasFence()) {
            glFinishFenceAPPLE(display->opengl_context_manager->getFence());
        }

        MWTime now = display->clock->getCurrentTimeUS();
        stimDisplayUpdate->setValue(display->getAnnounceData(), now);
        display->announceDisplayStack(now);
    }
}

void StimulusDisplay::announceDisplayStack(MWTime time) {
    shared_ptr<StimulusNode> node = display_stack->getBackmost(); //tail;

    while(node != shared_ptr< LinkedListNode<StimulusNode> >()) {
		if(node->isVisible()) {
            node->announceStimulusDraw(time);
		}

		node = node->getPrevious();
    }

}

Datum StimulusDisplay::getAnnounceData() {
    shared_ptr<StimulusNode> node = display_stack->getBackmost(); //tail;

    Datum stimAnnounce(M_LIST, 1);
    while(node != shared_ptr< LinkedListNode<StimulusNode> >()) {
		if(node->isVisible()) {
            Datum individualAnnounce(node->getCurrentAnnounceDrawData());
			if(!individualAnnounce.isUndefined()) {
				stimAnnounce.addElement(individualAnnounce);
			}
		}

		node = node->getPrevious();
    }
	return stimAnnounce;
}

shared_ptr<StimulusDisplay> StimulusDisplay::getCurrentStimulusDisplay() {
    if (!GlobalCurrentExperiment) {
        throw SimpleException("no experiment currently defined");
    }

    shared_ptr<StimulusDisplay> currentDisplay = GlobalCurrentExperiment->getStimulusDisplay();
    if (!currentDisplay) {
        throw SimpleException("no stimulus display in current experiment");
    }

    return currentDisplay;
}

<|endoftext|>