<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/process_singleton.h"

#include <shellapi.h>

#include "base/base_paths.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/process/process.h"
#include "base/process/process_info.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/win/registry.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "chrome/browser/chrome_process_finder_win.h"
#include "content/public/common/result_codes.h"
#include "net/base/escape.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/win/hwnd_util.h"

namespace {

const char kLockfile[] = "lockfile";

// A helper class that acquires the given |mutex| while the AutoLockMutex is in
// scope.
class AutoLockMutex {
 public:
  explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) {
    DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
    DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
  }

  ~AutoLockMutex() {
    BOOL released = ::ReleaseMutex(mutex_);
    DPCHECK(released);
  }

 private:
  HANDLE mutex_;
  DISALLOW_COPY_AND_ASSIGN(AutoLockMutex);
};

// A helper class that releases the given |mutex| while the AutoUnlockMutex is
// in scope and immediately re-acquires it when going out of scope.
class AutoUnlockMutex {
 public:
  explicit AutoUnlockMutex(HANDLE mutex) : mutex_(mutex) {
    BOOL released = ::ReleaseMutex(mutex_);
    DPCHECK(released);
  }

  ~AutoUnlockMutex() {
    DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
    DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
  }

 private:
  HANDLE mutex_;
  DISALLOW_COPY_AND_ASSIGN(AutoUnlockMutex);
};

// Checks the visibility of the enumerated window and signals once a visible
// window has been found.
BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
  bool* result = reinterpret_cast<bool*>(param);
  *result = ::IsWindowVisible(window) != 0;
  // Stops enumeration if a visible window has been found.
  return !*result;
}

// Convert Command line string to argv.
base::CommandLine::StringVector CommandLineStringToArgv(
    const std::wstring& command_line_string) {
  int num_args = 0;
  wchar_t** args = NULL;
  args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args);
  base::CommandLine::StringVector argv;
  for (int i = 0; i < num_args; ++i)
    argv.push_back(std::wstring(args[i]));
  LocalFree(args);
  return argv;
}

bool ParseCommandLine(const COPYDATASTRUCT* cds,
                      base::CommandLine::StringVector* parsed_command_line,
                      base::FilePath* current_directory) {
  // We should have enough room for the shortest command (min_message_size)
  // and also be a multiple of wchar_t bytes. The shortest command
  // possible is L"START\0\0" (empty current directory and command line).
  static const int min_message_size = 7;
  if (cds->cbData < min_message_size * sizeof(wchar_t) ||
      cds->cbData % sizeof(wchar_t) != 0) {
    LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
    return false;
  }

  // We split the string into 4 parts on NULLs.
  DCHECK(cds->lpData);
  const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
                         cds->cbData / sizeof(wchar_t));
  const std::wstring::size_type first_null = msg.find_first_of(L'\0');
  if (first_null == 0 || first_null == std::wstring::npos) {
    // no NULL byte, don't know what to do
    LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() <<
      ", first null = " << first_null;
    return false;
  }

  // Decode the command, which is everything until the first NULL.
  if (msg.substr(0, first_null) == L"START") {
    // Another instance is starting parse the command line & do what it would
    // have done.
    VLOG(1) << "Handling STARTUP request from another process";
    const std::wstring::size_type second_null =
        msg.find_first_of(L'\0', first_null + 1);
    if (second_null == std::wstring::npos ||
        first_null == msg.length() - 1 || second_null == msg.length()) {
      LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
        "parts separated by NULLs";
      return false;
    }

    // Get current directory.
    *current_directory = base::FilePath(msg.substr(first_null + 1,
                                                   second_null - first_null));

    const std::wstring::size_type third_null =
        msg.find_first_of(L'\0', second_null + 1);
    if (third_null == std::wstring::npos ||
        third_null == msg.length()) {
      LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
        "parts separated by NULLs";
    }

    // Get command line.
    const std::wstring cmd_line =
        msg.substr(second_null + 1, third_null - second_null);
    *parsed_command_line = CommandLineStringToArgv(cmd_line);
    return true;
  }
  return false;
}

bool ProcessLaunchNotification(
    const ProcessSingleton::NotificationCallback& notification_callback,
    UINT message,
    WPARAM wparam,
    LPARAM lparam,
    LRESULT* result) {
  if (message != WM_COPYDATA)
    return false;

  // Handle the WM_COPYDATA message from another process.
  const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);

  base::CommandLine::StringVector parsed_command_line;
  base::FilePath current_directory;
  if (!ParseCommandLine(cds, &parsed_command_line, &current_directory)) {
    *result = TRUE;
    return true;
  }

  *result = notification_callback.Run(parsed_command_line, current_directory) ?
      TRUE : FALSE;
  return true;
}

bool TerminateAppWithError() {
  // TODO: This is called when the secondary process can't ping the primary
  // process. Need to find out what to do here.
  return false;
}

}  // namespace

ProcessSingleton::ProcessSingleton(
    const base::FilePath& user_data_dir,
    const NotificationCallback& notification_callback)
    : notification_callback_(notification_callback),
      is_virtualized_(false),
      lock_file_(INVALID_HANDLE_VALUE),
      user_data_dir_(user_data_dir),
      should_kill_remote_process_callback_(
          base::Bind(&TerminateAppWithError)) {
  // The user_data_dir may have not been created yet.
  base::CreateDirectoryAndGetError(user_data_dir, nullptr);
}

ProcessSingleton::~ProcessSingleton() {
  if (lock_file_ != INVALID_HANDLE_VALUE)
    ::CloseHandle(lock_file_);
}

// Code roughly based on Mozilla.
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
  if (is_virtualized_)
    return PROCESS_NOTIFIED;  // We already spawned the process in this case.
  if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) {
    return LOCK_ERROR;
  } else if (!remote_window_) {
    return PROCESS_NONE;
  }

  switch (chrome::AttemptToNotifyRunningChrome(remote_window_, false)) {
    case chrome::NOTIFY_SUCCESS:
      return PROCESS_NOTIFIED;
    case chrome::NOTIFY_FAILED:
      remote_window_ = NULL;
      return PROCESS_NONE;
    case chrome::NOTIFY_WINDOW_HUNG:
      // Fall through and potentially terminate the hung browser.
      break;
  }

  DWORD process_id = 0;
  DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id);
  if (!thread_id || !process_id) {
    remote_window_ = NULL;
    return PROCESS_NONE;
  }
  base::Process process = base::Process::Open(process_id);

  // The window is hung. Scan for every window to find a visible one.
  bool visible_window = false;
  ::EnumThreadWindows(thread_id,
                      &BrowserWindowEnumeration,
                      reinterpret_cast<LPARAM>(&visible_window));

  // If there is a visible browser window, ask the user before killing it.
  if (visible_window && !should_kill_remote_process_callback_.Run()) {
    // The user denied. Quit silently.
    return PROCESS_NOTIFIED;
  }

  // Time to take action. Kill the browser process.
  process.Terminate(content::RESULT_CODE_HUNG, true);
  remote_window_ = NULL;
  return PROCESS_NONE;
}

ProcessSingleton::NotifyResult
ProcessSingleton::NotifyOtherProcessOrCreate() {
  ProcessSingleton::NotifyResult result = PROCESS_NONE;
  if (!Create()) {
    result = NotifyOtherProcess();
    if (result == PROCESS_NONE)
      result = PROFILE_IN_USE;
  }
  return result;
}

// Look for a Chrome instance that uses the same profile directory. If there
// isn't one, create a message window with its title set to the profile
// directory path.
bool ProcessSingleton::Create() {
  static const wchar_t kMutexName[] = L"Local\\AtomProcessSingletonStartup!";

  remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
  if (!remote_window_) {
    // Make sure we will be the one and only process creating the window.
    // We use a named Mutex since we are protecting against multi-process
    // access. As documented, it's clearer to NOT request ownership on creation
    // since it isn't guaranteed we will get it. It is better to create it
    // without ownership and explicitly get the ownership afterward.
    base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
    if (!only_me.IsValid()) {
      DPLOG(FATAL) << "CreateMutex failed";
      return false;
    }

    AutoLockMutex auto_lock_only_me(only_me.Get());

    // We now own the mutex so we are the only process that can create the
    // window at this time, but we must still check if someone created it
    // between the time where we looked for it above and the time the mutex
    // was given to us.
    remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
    if (!remote_window_) {
      // We have to make sure there is no Chrome instance running on another
      // machine that uses the same profile.
      base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile);
      lock_file_ = ::CreateFile(lock_file_path.value().c_str(),
                                GENERIC_WRITE,
                                FILE_SHARE_READ,
                                NULL,
                                CREATE_ALWAYS,
                                FILE_ATTRIBUTE_NORMAL |
                                FILE_FLAG_DELETE_ON_CLOSE,
                                NULL);
      DWORD error = ::GetLastError();
      LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE &&
          error == ERROR_ALREADY_EXISTS) << "Lock file exists but is writable.";
      LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE)
          << "Lock file can not be created! Error code: " << error;

      if (lock_file_ != INVALID_HANDLE_VALUE) {
        // Set the window's title to the path of our user data directory so
        // other Chrome instances can decide if they should forward to us.
        bool result = window_.CreateNamed(
            base::Bind(&ProcessLaunchNotification, notification_callback_),
            user_data_dir_.value());

        // NB: Ensure that if the primary app gets started as elevated
        // admin inadvertently, secondary windows running not as elevated
        // will still be able to send messages
        ::ChangeWindowMessageFilterEx(window_.hwnd(), WM_COPYDATA, MSGFLT_ALLOW, NULL);
        CHECK(result && window_.hwnd());
      }
    }
  }

  return window_.hwnd() != NULL;
}

void ProcessSingleton::Cleanup() {
}

void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting(
    const ShouldKillRemoteProcessCallback& display_dialog_callback) {
  should_kill_remote_process_callback_ = display_dialog_callback;
}
<commit_msg>fix process finder path<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/process_singleton.h"

#include <shellapi.h>

#include "base/base_paths.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/process/process.h"
#include "base/process/process_info.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/win/registry.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "chrome/browser/win/chrome_process_finder.h"
#include "content/public/common/result_codes.h"
#include "net/base/escape.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/win/hwnd_util.h"

namespace {

const char kLockfile[] = "lockfile";

// A helper class that acquires the given |mutex| while the AutoLockMutex is in
// scope.
class AutoLockMutex {
 public:
  explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) {
    DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
    DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
  }

  ~AutoLockMutex() {
    BOOL released = ::ReleaseMutex(mutex_);
    DPCHECK(released);
  }

 private:
  HANDLE mutex_;
  DISALLOW_COPY_AND_ASSIGN(AutoLockMutex);
};

// A helper class that releases the given |mutex| while the AutoUnlockMutex is
// in scope and immediately re-acquires it when going out of scope.
class AutoUnlockMutex {
 public:
  explicit AutoUnlockMutex(HANDLE mutex) : mutex_(mutex) {
    BOOL released = ::ReleaseMutex(mutex_);
    DPCHECK(released);
  }

  ~AutoUnlockMutex() {
    DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
    DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
  }

 private:
  HANDLE mutex_;
  DISALLOW_COPY_AND_ASSIGN(AutoUnlockMutex);
};

// Checks the visibility of the enumerated window and signals once a visible
// window has been found.
BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
  bool* result = reinterpret_cast<bool*>(param);
  *result = ::IsWindowVisible(window) != 0;
  // Stops enumeration if a visible window has been found.
  return !*result;
}

// Convert Command line string to argv.
base::CommandLine::StringVector CommandLineStringToArgv(
    const std::wstring& command_line_string) {
  int num_args = 0;
  wchar_t** args = NULL;
  args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args);
  base::CommandLine::StringVector argv;
  for (int i = 0; i < num_args; ++i)
    argv.push_back(std::wstring(args[i]));
  LocalFree(args);
  return argv;
}

bool ParseCommandLine(const COPYDATASTRUCT* cds,
                      base::CommandLine::StringVector* parsed_command_line,
                      base::FilePath* current_directory) {
  // We should have enough room for the shortest command (min_message_size)
  // and also be a multiple of wchar_t bytes. The shortest command
  // possible is L"START\0\0" (empty current directory and command line).
  static const int min_message_size = 7;
  if (cds->cbData < min_message_size * sizeof(wchar_t) ||
      cds->cbData % sizeof(wchar_t) != 0) {
    LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
    return false;
  }

  // We split the string into 4 parts on NULLs.
  DCHECK(cds->lpData);
  const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
                         cds->cbData / sizeof(wchar_t));
  const std::wstring::size_type first_null = msg.find_first_of(L'\0');
  if (first_null == 0 || first_null == std::wstring::npos) {
    // no NULL byte, don't know what to do
    LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() <<
      ", first null = " << first_null;
    return false;
  }

  // Decode the command, which is everything until the first NULL.
  if (msg.substr(0, first_null) == L"START") {
    // Another instance is starting parse the command line & do what it would
    // have done.
    VLOG(1) << "Handling STARTUP request from another process";
    const std::wstring::size_type second_null =
        msg.find_first_of(L'\0', first_null + 1);
    if (second_null == std::wstring::npos ||
        first_null == msg.length() - 1 || second_null == msg.length()) {
      LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
        "parts separated by NULLs";
      return false;
    }

    // Get current directory.
    *current_directory = base::FilePath(msg.substr(first_null + 1,
                                                   second_null - first_null));

    const std::wstring::size_type third_null =
        msg.find_first_of(L'\0', second_null + 1);
    if (third_null == std::wstring::npos ||
        third_null == msg.length()) {
      LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
        "parts separated by NULLs";
    }

    // Get command line.
    const std::wstring cmd_line =
        msg.substr(second_null + 1, third_null - second_null);
    *parsed_command_line = CommandLineStringToArgv(cmd_line);
    return true;
  }
  return false;
}

bool ProcessLaunchNotification(
    const ProcessSingleton::NotificationCallback& notification_callback,
    UINT message,
    WPARAM wparam,
    LPARAM lparam,
    LRESULT* result) {
  if (message != WM_COPYDATA)
    return false;

  // Handle the WM_COPYDATA message from another process.
  const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);

  base::CommandLine::StringVector parsed_command_line;
  base::FilePath current_directory;
  if (!ParseCommandLine(cds, &parsed_command_line, &current_directory)) {
    *result = TRUE;
    return true;
  }

  *result = notification_callback.Run(parsed_command_line, current_directory) ?
      TRUE : FALSE;
  return true;
}

bool TerminateAppWithError() {
  // TODO: This is called when the secondary process can't ping the primary
  // process. Need to find out what to do here.
  return false;
}

}  // namespace

ProcessSingleton::ProcessSingleton(
    const base::FilePath& user_data_dir,
    const NotificationCallback& notification_callback)
    : notification_callback_(notification_callback),
      is_virtualized_(false),
      lock_file_(INVALID_HANDLE_VALUE),
      user_data_dir_(user_data_dir),
      should_kill_remote_process_callback_(
          base::Bind(&TerminateAppWithError)) {
  // The user_data_dir may have not been created yet.
  base::CreateDirectoryAndGetError(user_data_dir, nullptr);
}

ProcessSingleton::~ProcessSingleton() {
  if (lock_file_ != INVALID_HANDLE_VALUE)
    ::CloseHandle(lock_file_);
}

// Code roughly based on Mozilla.
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
  if (is_virtualized_)
    return PROCESS_NOTIFIED;  // We already spawned the process in this case.
  if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) {
    return LOCK_ERROR;
  } else if (!remote_window_) {
    return PROCESS_NONE;
  }

  switch (chrome::AttemptToNotifyRunningChrome(remote_window_, false)) {
    case chrome::NOTIFY_SUCCESS:
      return PROCESS_NOTIFIED;
    case chrome::NOTIFY_FAILED:
      remote_window_ = NULL;
      return PROCESS_NONE;
    case chrome::NOTIFY_WINDOW_HUNG:
      // Fall through and potentially terminate the hung browser.
      break;
  }

  DWORD process_id = 0;
  DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id);
  if (!thread_id || !process_id) {
    remote_window_ = NULL;
    return PROCESS_NONE;
  }
  base::Process process = base::Process::Open(process_id);

  // The window is hung. Scan for every window to find a visible one.
  bool visible_window = false;
  ::EnumThreadWindows(thread_id,
                      &BrowserWindowEnumeration,
                      reinterpret_cast<LPARAM>(&visible_window));

  // If there is a visible browser window, ask the user before killing it.
  if (visible_window && !should_kill_remote_process_callback_.Run()) {
    // The user denied. Quit silently.
    return PROCESS_NOTIFIED;
  }

  // Time to take action. Kill the browser process.
  process.Terminate(content::RESULT_CODE_HUNG, true);
  remote_window_ = NULL;
  return PROCESS_NONE;
}

ProcessSingleton::NotifyResult
ProcessSingleton::NotifyOtherProcessOrCreate() {
  ProcessSingleton::NotifyResult result = PROCESS_NONE;
  if (!Create()) {
    result = NotifyOtherProcess();
    if (result == PROCESS_NONE)
      result = PROFILE_IN_USE;
  }
  return result;
}

// Look for a Chrome instance that uses the same profile directory. If there
// isn't one, create a message window with its title set to the profile
// directory path.
bool ProcessSingleton::Create() {
  static const wchar_t kMutexName[] = L"Local\\AtomProcessSingletonStartup!";

  remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
  if (!remote_window_) {
    // Make sure we will be the one and only process creating the window.
    // We use a named Mutex since we are protecting against multi-process
    // access. As documented, it's clearer to NOT request ownership on creation
    // since it isn't guaranteed we will get it. It is better to create it
    // without ownership and explicitly get the ownership afterward.
    base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
    if (!only_me.IsValid()) {
      DPLOG(FATAL) << "CreateMutex failed";
      return false;
    }

    AutoLockMutex auto_lock_only_me(only_me.Get());

    // We now own the mutex so we are the only process that can create the
    // window at this time, but we must still check if someone created it
    // between the time where we looked for it above and the time the mutex
    // was given to us.
    remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
    if (!remote_window_) {
      // We have to make sure there is no Chrome instance running on another
      // machine that uses the same profile.
      base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile);
      lock_file_ = ::CreateFile(lock_file_path.value().c_str(),
                                GENERIC_WRITE,
                                FILE_SHARE_READ,
                                NULL,
                                CREATE_ALWAYS,
                                FILE_ATTRIBUTE_NORMAL |
                                FILE_FLAG_DELETE_ON_CLOSE,
                                NULL);
      DWORD error = ::GetLastError();
      LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE &&
          error == ERROR_ALREADY_EXISTS) << "Lock file exists but is writable.";
      LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE)
          << "Lock file can not be created! Error code: " << error;

      if (lock_file_ != INVALID_HANDLE_VALUE) {
        // Set the window's title to the path of our user data directory so
        // other Chrome instances can decide if they should forward to us.
        bool result = window_.CreateNamed(
            base::Bind(&ProcessLaunchNotification, notification_callback_),
            user_data_dir_.value());

        // NB: Ensure that if the primary app gets started as elevated
        // admin inadvertently, secondary windows running not as elevated
        // will still be able to send messages
        ::ChangeWindowMessageFilterEx(window_.hwnd(), WM_COPYDATA, MSGFLT_ALLOW, NULL);
        CHECK(result && window_.hwnd());
      }
    }
  }

  return window_.hwnd() != NULL;
}

void ProcessSingleton::Cleanup() {
}

void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting(
    const ShouldKillRemoteProcessCallback& display_dialog_callback) {
  should_kill_remote_process_callback_ = display_dialog_callback;
}
<|endoftext|>


<commit_before>/*
 * AbstractView_EventConsumption.cpp
 *
 * Copyright (C) 2020 by Universitaet Stuttgart (VIS).
 * Alle Rechte vorbehalten.
 */

#include "mmcore/view/AbstractView_EventConsumption.h"

#include "Framebuffer_Events.h"
#include "KeyboardMouse_Events.h"
#include "Window_Events.h"
#include "IOpenGL_Context.h"

#include <chrono>

namespace megamol {
namespace core {
namespace view {

using namespace megamol::module_resources;

// shorthand notation to unpack a ModuleResource to some type.
// if the type is present in the resource is made available as an 'events' variable in the if statemtnt.
// note that when using this macro there is no visible opening bracket { for the if statements because it is hidden inside the macro
#define GET_RESOURCE(TYPENAME) \
    { \
		TYPENAME const& events = resource.getResource<TYPENAME>();

void view_consume_keyboard_events(AbstractView& view, megamol::frontend::ModuleResource const& resource) {
    GET_RESOURCE(KeyboardEvents)//{
		for (auto& e : events.key_events)
			view.OnKey(std::get<0>(e), std::get<1>(e), std::get<2>(e));

		for (auto& e : events.codepoint_events)
			view.OnChar(e);
	}
}

void view_consume_mouse_events(AbstractView& view, megamol::frontend::ModuleResource const& resource) {
    GET_RESOURCE(MouseEvents)//{
		for (auto& e : events.buttons_events)
			view.OnMouseButton(std::get<0>(e), std::get<1>(e), std::get<2>(e));

		for (auto& e : events.position_events)
			view.OnMouseMove(std::get<0>(e), std::get<1>(e));

		for (auto& e : events.scroll_events)
			view.OnMouseScroll(std::get<0>(e), std::get<1>(e));

		//for (auto& e: events.enter_events) {}
	}
}

void view_consume_window_events(AbstractView& view, megamol::frontend::ModuleResource const& resource) {
    GET_RESOURCE(WindowEvents)//{
		events.is_focused_events;
	}
}

void view_consume_framebuffer_events(AbstractView& view, megamol::frontend::ModuleResource const& resource) {
    GET_RESOURCE(FramebufferEvents)//{
        /// XXX Permanent resize might be required for new views getting right size without new framebuffer event
        view.Resize(static_cast<unsigned int>(events.previous_state.width), static_cast<unsigned int>(events.previous_state.height));
		//for (auto& e: events.size_events)
		//	view.Resize(static_cast<unsigned int>(e.width), static_cast<unsigned int>(e.height));
    }
}

// this is a weird place to measure passed program time, but we do it here so we satisfy _mmcRenderViewContext and nobody else needs to know
static std::chrono::high_resolution_clock::time_point render_view_context_timer_start;

void view_poke_rendering(AbstractView& view, megamol::frontend::ModuleResource const& resource) {
    megamol::module_resources::IOpenGL_Context const * maybe_opengl = nullptr;

    if (resource.getIdentifier() == "IOpenGL_Context")
		maybe_opengl = &resource.getResource<megamol::module_resources::IOpenGL_Context>();

	static bool started_timer = false;
	if (!started_timer) {
		render_view_context_timer_start = std::chrono::high_resolution_clock::now();
        started_timer = true;
	}

	const auto render = [&]() {
		_mmcRenderViewContext dummyRenderViewContext = {}; // zero initialization. the struct is currently only used for time propagation.

        const double time = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::high_resolution_clock::now() - render_view_context_timer_start)
            .count() / static_cast<double>(1000);

        dummyRenderViewContext.InstanceTime = time;
        dummyRenderViewContext.Time = time;

		view.Render(dummyRenderViewContext);
	};

    if (maybe_opengl) {
		maybe_opengl->activate(); // makes GL context current

		render();

		maybe_opengl->close();
	} else {
		render();
	}
}

std::vector<std::string> get_gl_view_runtime_resources_requests() {
    return {"KeyboardEvents", "MouseEvents", "WindowEvents", "FramebufferEvents", "IOpenGL_Context"};
}

bool view_rendering_execution(megamol::core::Module::ptr_type module_ptr, std::vector<megamol::frontend::ModuleResource> const& resources) {
	megamol::core::view::AbstractView* view_ptr =
		dynamic_cast<megamol::core::view::AbstractView*>(module_ptr.get());

	if (!view_ptr) {
		std::cout << "error. module is not a view module. could not set as graph rendering entry point." << std::endl;
		return false;
	}

	megamol::core::view::AbstractView& view = *view_ptr;

	// resources are in order of initial requests from get_gl_view_runtime_resources_requests()
	megamol::core::view::view_consume_keyboard_events(view, resources[0]);
	megamol::core::view::view_consume_mouse_events(view, resources[1]);
	megamol::core::view::view_consume_window_events(view, resources[2]);
	megamol::core::view::view_consume_framebuffer_events(view, resources[3]);
	megamol::core::view::view_poke_rendering(view, resources[4]);

	return true;
}

} /* end namespace view */
} /* end namespace core */
} /* end namespace megamol */

<commit_msg>I think we have partial time<commit_after>/*
 * AbstractView_EventConsumption.cpp
 *
 * Copyright (C) 2020 by Universitaet Stuttgart (VIS).
 * Alle Rechte vorbehalten.
 */

#include "mmcore/view/AbstractView_EventConsumption.h"

#include "Framebuffer_Events.h"
#include "KeyboardMouse_Events.h"
#include "Window_Events.h"
#include "IOpenGL_Context.h"

#include <chrono>

namespace megamol {
namespace core {
namespace view {

using namespace megamol::module_resources;

// shorthand notation to unpack a ModuleResource to some type.
// if the type is present in the resource is made available as an 'events' variable in the if statemtnt.
// note that when using this macro there is no visible opening bracket { for the if statements because it is hidden inside the macro
#define GET_RESOURCE(TYPENAME) \
    { \
		TYPENAME const& events = resource.getResource<TYPENAME>();

void view_consume_keyboard_events(AbstractView& view, megamol::frontend::ModuleResource const& resource) {
    GET_RESOURCE(KeyboardEvents)//{
		for (auto& e : events.key_events)
			view.OnKey(std::get<0>(e), std::get<1>(e), std::get<2>(e));

		for (auto& e : events.codepoint_events)
			view.OnChar(e);
	}
}

void view_consume_mouse_events(AbstractView& view, megamol::frontend::ModuleResource const& resource) {
    GET_RESOURCE(MouseEvents)//{
		for (auto& e : events.buttons_events)
			view.OnMouseButton(std::get<0>(e), std::get<1>(e), std::get<2>(e));

		for (auto& e : events.position_events)
			view.OnMouseMove(std::get<0>(e), std::get<1>(e));

		for (auto& e : events.scroll_events)
			view.OnMouseScroll(std::get<0>(e), std::get<1>(e));

		//for (auto& e: events.enter_events) {}
	}
}

void view_consume_window_events(AbstractView& view, megamol::frontend::ModuleResource const& resource) {
    GET_RESOURCE(WindowEvents)//{
		events.is_focused_events;
	}
}

void view_consume_framebuffer_events(AbstractView& view, megamol::frontend::ModuleResource const& resource) {
    GET_RESOURCE(FramebufferEvents)//{
        /// XXX Permanent resize might be required for new views getting right size without new framebuffer event
        view.Resize(static_cast<unsigned int>(events.previous_state.width), static_cast<unsigned int>(events.previous_state.height));
		//for (auto& e: events.size_events)
		//	view.Resize(static_cast<unsigned int>(e.width), static_cast<unsigned int>(e.height));
    }
}

// this is a weird place to measure passed program time, but we do it here so we satisfy _mmcRenderViewContext and nobody else needs to know
static std::chrono::high_resolution_clock::time_point render_view_context_timer_start;

void view_poke_rendering(AbstractView& view, megamol::frontend::ModuleResource const& resource) {
    megamol::module_resources::IOpenGL_Context const * maybe_opengl = nullptr;

    if (resource.getIdentifier() == "IOpenGL_Context")
		maybe_opengl = &resource.getResource<megamol::module_resources::IOpenGL_Context>();

	static bool started_timer = false;
	if (!started_timer) {
		render_view_context_timer_start = std::chrono::high_resolution_clock::now();
        started_timer = true;
	}

	const auto render = [&]() {
		_mmcRenderViewContext dummyRenderViewContext = {}; // zero initialization. the struct is currently only used for time propagation.

        const double time = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::high_resolution_clock::now() - render_view_context_timer_start)
            .count() / static_cast<double>(1000);

            // this is missing!
            // auto core = view->View()->GetCoreInstance();
            // core->SetFrameID(frameID);
            dummyRenderViewContext.InstanceTime = time;
            dummyRenderViewContext.Time = view.DefaultTime(time);

		view.Render(dummyRenderViewContext);
	};

    if (maybe_opengl) {
		maybe_opengl->activate(); // makes GL context current

		render();

		maybe_opengl->close();
	} else {
		render();
	}
}

std::vector<std::string> get_gl_view_runtime_resources_requests() {
    return {"KeyboardEvents", "MouseEvents", "WindowEvents", "FramebufferEvents", "IOpenGL_Context"};
}

bool view_rendering_execution(megamol::core::Module::ptr_type module_ptr, std::vector<megamol::frontend::ModuleResource> const& resources) {
	megamol::core::view::AbstractView* view_ptr =
		dynamic_cast<megamol::core::view::AbstractView*>(module_ptr.get());

	if (!view_ptr) {
		std::cout << "error. module is not a view module. could not set as graph rendering entry point." << std::endl;
		return false;
	}

	megamol::core::view::AbstractView& view = *view_ptr;

	// resources are in order of initial requests from get_gl_view_runtime_resources_requests()
	megamol::core::view::view_consume_keyboard_events(view, resources[0]);
	megamol::core::view::view_consume_mouse_events(view, resources[1]);
	megamol::core::view::view_consume_window_events(view, resources[2]);
	megamol::core::view::view_consume_framebuffer_events(view, resources[3]);
	megamol::core::view::view_poke_rendering(view, resources[4]);

	return true;
}

} /* end namespace view */
} /* end namespace core */
} /* end namespace megamol */

<|endoftext|>


<commit_before>void Config()
{
  //
  // Set Random Number seed
  gRandom->SetSeed(12345);

  // ThetaRange is (0., 180.). It was (0.28,179.72) 7/12/00 09:00
  new AliGeant3("C++ Interface to Geant3");

  //=======================================================================
  //  Create the output file

  TFile *rootfile = new TFile("galice.root","recreate");
  rootfile->SetCompressionLevel(2);
  TGeant3 *geant3 = (TGeant3*)gMC;

  //
  // Set External decayer
  AliDecayer* decayer = new AliDecayerPythia();
  decayer->SetForceDecay(kAll);
  decayer->Init();
  gMC->SetExternalDecayer(decayer);

  //
  //
  //=======================================================================
  // ******* GEANT STEERING parameters FOR ALICE SIMULATION *******
  geant3->SetTRIG(1); //Number of events to be processed
  geant3->SetSWIT(4,10);
  geant3->SetDEBU(0,0,1);
  //geant3->SetSWIT(2,2);
  geant3->SetDCAY(1);
  geant3->SetPAIR(1);
  geant3->SetCOMP(1);
  geant3->SetPHOT(1);
  geant3->SetPFIS(0);
  geant3->SetDRAY(0);
  geant3->SetANNI(1);
  geant3->SetBREM(1);
  geant3->SetMUNU(1);
  geant3->SetCKOV(1);
  geant3->SetHADR(1); //Select pure GEANH (HADR 1) or GEANH/NUCRIN (HADR 3)
  geant3->SetLOSS(2);
  geant3->SetMULS(1);
  geant3->SetRAYL(1);
  geant3->SetAUTO(1); //Select automatic STMIN etc... calc. (AUTO 1) or manual (AUTO 0)
  geant3->SetABAN(0); //Restore 3.16 behaviour for abandoned tracks
  geant3->SetOPTI(2); //Select optimisation level for GEANT geometry searches (0,1,2)
  geant3->SetERAN(5.e-7);

  Float_t cut    = 1.e-3; // 1MeV cut by default
  Float_t tofmax = 1.e10;
  //             GAM ELEC NHAD CHAD MUON EBREM MUHAB EDEL MUDEL MUPA TOFMAX
  geant3->SetCUTS(cut,cut, cut, cut, cut, cut,  cut,  cut, cut,  cut, tofmax);
  //
  //=======================================================================
  // ************* STEERING parameters FOR ALICE SIMULATION **************
  // --- Specify event type to be tracked through the ALICE setup
  // --- All positions are in cm, angles in degrees, and P and E in GeV
  AliGenHIJINGpara *gener = new AliGenHIJINGpara(84210);
  gener->SetMomentumRange(0,999);
  gener->SetPhiRange(0,360);
  //  gener->SetThetaRange(0.28,179.72);
  gener->SetThetaRange(0.,180.);
  gener->SetOrigin(0,0,0);        //vertex position
  gener->SetSigma(0,0,0);         //Sigma in (X,Y,Z) (cm) on IP position
  gener->Init();
  //
  // Activate this line if you want the vertex smearing to happen
  // track by track
  //
  //gener->SetVertexSmear(perTrack);

  gAlice->SetField(-999,2);    //Specify maximum magnetic field in Tesla (neg. ==> default field)

  Int_t iABSO=1;
  Int_t iCASTOR=1;
  Int_t iDIPO=1;
  Int_t iFMD=1;
  Int_t iFRAME=1;
  Int_t iHALL=1;
  Int_t iITS=1;
  Int_t iMAG=1;
  Int_t iMUON=1;
  Int_t iPHOS=1;
  Int_t iPIPE=1;
  Int_t iPMD=1;
  Int_t iRICH=1;
  Int_t iSHIL=1;
  Int_t iSTART=1;
  Int_t iTOF=1;
  Int_t iTPC=1;
  Int_t iTRD=1;
  Int_t iZDC=1;

  //=================== Alice BODY parameters =============================
  AliBODY *BODY = new AliBODY("BODY","Alice envelop");

  if(iMAG) {
    //=================== MAG parameters ============================
    // --- Start with Magnet since detector layouts may be depending ---
    // --- on the selected Magnet dimensions ---
    AliMAG *MAG  = new AliMAG("MAG","Magnet");
  }

  if(iABSO) {
    //=================== ABSO parameters ============================
    AliABSO *ABSO  = new AliABSOv0("ABSO","Muon Absorber");
  }

  if(iDIPO) {
    //=================== DIPO parameters ============================

    AliDIPO *DIPO  = new AliDIPOv2("DIPO","Dipole version 2");
  }

  if(iHALL) {
    //=================== HALL parameters ============================

    AliHALL *HALL  = new AliHALL("HALL","Alice Hall");
  }

  if(iFRAME) {
    //=================== FRAME parameters ============================

    AliFRAME *FRAME  = new AliFRAMEv2("FRAME","Space Frame");

  }

  if(iSHIL) {
    //=================== SHIL parameters ============================

    AliSHIL *SHIL  = new AliSHILv0("SHIL","Shielding");
  }

  if(iPIPE) {
    //=================== PIPE parameters ============================

    AliPIPE *PIPE  = new AliPIPEv0("PIPE","Beam Pipe");
  }

  if(iITS) {

//=================== ITS parameters ============================
    //
    // As the innermost detector in ALICE, the Inner Tracking System "impacts" on
    // almost all other detectors. This involves the fact that the ITS geometry
    // still has several options to be followed in parallel in order to determine
    // the best set-up which minimizes the induced background. All the geometries
    // available to date are described in the following. Read carefully the comments
    // and use the default version (the only one uncommented) unless you are making
    // comparisons and you know what you are doing. In this case just uncomment the
    // ITS geometry you want to use and run Aliroot.
    //
    // Detailed geometries:
    //
    //
    //AliITS *ITS  = new AliITSv5symm("ITS","Updated ITS TDR detailed version with symmetric services");
    //
    //AliITS *ITS  = new AliITSv5asymm("ITS","Updates ITS TDR detailed version with asymmetric services");
    //
    AliITSvPPRasymm *ITS  = new AliITSvPPRasymm("ITS","New ITS PPR detailed version with asymmetric services");
    ITS->SetMinorVersion(2);                                         // don't touch this parameter if you're not an ITS developer
    ITS->SetReadDet(kFALSE);                                         // don't touch this parameter if you're not an ITS developer
    ITS->SetWriteDet("$ALICE_ROOT/ITS/ITSgeometry_vPPRasymm2.det");  // don't touch this parameter if you're not an ITS developer
    ITS->SetThicknessDet1(300.);   // detector thickness on layer 1 must be in the range [100,300]
    ITS->SetThicknessDet2(300.);   // detector thickness on layer 2 must be in the range [100,300]
    ITS->SetThicknessChip1(300.);  // chip thickness on layer 1 must be in the range [150,300]
    ITS->SetThicknessChip2(300.);  // chip thickness on layer 2 must be in the range [150,300]
    ITS->SetRails(1);	           // 1 --> rails in ; 0 --> rails out
    ITS->SetCoolingFluid(1);       // 1 --> water ; 0 --> freon
    //
    //AliITSvPPRsymm *ITS  = new AliITSvPPRsymm("ITS","New ITS PPR detailed version with symmetric services");
    //ITS->SetMinorVersion(2);                                       // don't touch this parameter if you're not an ITS developer
    //ITS->SetReadDet(kFALSE);                                       // don't touch this parameter if you're not an ITS developer
    //ITS->SetWriteDet("$ALICE_ROOT/ITS/ITSgeometry_vPPRsymm2.det"); // don't touch this parameter if you're not an ITS developer
    //ITS->SetThicknessDet1(300.);   // detector thickness on layer 1 must be in the range [100,300]
    //ITS->SetThicknessDet2(300.);   // detector thickness on layer 2 must be in the range [100,300]
    //ITS->SetThicknessChip1(300.);  // chip thickness on layer 1 must be in the range [150,300]
    //ITS->SetThicknessChip2(300.);  // chip thickness on layer 2 must be in the range [150,300]
    //ITS->SetRails(1);              // 1 --> rails in ; 0 --> rails out
    //ITS->SetCoolingFluid(1);       // 1 --> water ; 0 --> freon
    //
    //
    // Coarse geometries (warning: no hits are produced with these coarse geometries and they unuseful
    // for reconstruction !):
    //
    //
    //AliITSvPPRcoarseasymm *ITS  = new AliITSvPPRcoarseasymm("ITS","New ITS PPR coarse version with asymmetric services");
    //ITS->SetRails(1);                // 1 --> rails in ; 0 --> rails out
    //ITS->SetSupportMaterial(0);      // 0 --> Copper ; 1 --> Aluminum ; 2 --> Carbon
    //
    //AliITS *ITS  = new AliITSvPPRcoarsesymm("ITS","New ITS PPR coarse version with symmetric services");
    //ITS->SetRails(1);                // 1 --> rails in ; 0 --> rails out
    //ITS->SetSupportMaterial(0);      // 0 --> Copper ; 1 --> Aluminum ; 2 --> Carbon
    //
    //
    //
    // Geant3 <-> EUCLID conversion
    // ============================
    //
    // SetEUCLID is a flag to output (=1) or not to output (=0) both geometry and
    // media to two ASCII files (called by default ITSgeometry.euc and
    // ITSgeometry.tme) in a format understandable to the CAD system EUCLID.
    // The default (=0) means that you dont want to use this facility.
    //
    ITS->SetEUCLID(0);
  }

  if(iTPC) {
    //============================ TPC parameters ================================
    // --- This allows the user to specify sectors for the SLOW (TPC geometry 2)
    // --- Simulator. SecAL (SecAU) <0 means that ALL lower (upper)
    // --- sectors are specified, any value other than that requires at least one
    // --- sector (lower or upper)to be specified!
    // --- Reminder: sectors 1-24 are lower sectors (1-12 -> z>0, 13-24 -> z<0)
    // ---           sectors 25-72 are the upper ones (25-48 -> z>0, 49-72 -> z<0)
    // --- SecLows - number of lower sectors specified (up to 6)
    // --- SecUps - number of upper sectors specified (up to 12)
    // --- Sens - sensitive strips for the Slow Simulator !!!
    // --- This does NOT work if all S or L-sectors are specified, i.e.
    // --- if SecAL or SecAU < 0
    //
    //
    //-----------------------------------------------------------------------------

    //  gROOT->LoadMacro("SetTPCParam.C");
    //  AliTPCParam *param = SetTPCParam();
    AliTPC *TPC  = new AliTPCv2("TPC","Default");
    // All sectors included
    TPC->SetSecAL(-1);
    TPC->SetSecAU(-1);

  }

  if(iTOF) {
    //=================== TOF parameters ============================
    AliTOF *TOF  = new AliTOFv2("TOF","normal TOF");
  }

  if(iRICH) {
    //=================== RICH parameters ===========================
    AliRICH *RICH  = new AliRICHv1("RICH","normal RICH");

  }

  if(iZDC) {
    //=================== ZDC parameters ============================

    AliZDC *ZDC  = new AliZDCv1("ZDC","normal ZDC");
  }

  if(iCASTOR) {
    //=================== CASTOR parameters ============================

    AliCASTOR *CASTOR  = new AliCASTORv1("CASTOR","normal CASTOR");
  }

  if(iTRD) {
    //=================== TRD parameters ============================

    AliTRD *TRD  = new AliTRDv1("TRD","TRD slow simulator");

    // Select the gas mixture (0: 97% Xe + 3% isobutane, 1: 90% Xe + 10% CO2)
    TRD->SetGasMix(1);

    // With hole in front of PHOS
    TRD->SetPHOShole();
    // With hole in front of RICH
    TRD->SetRICHhole();
    // Switch on TR
    AliTRDsim *TRDsim = TRD->CreateTR();
  }

  if(iFMD) {
    //=================== FMD parameters ============================

    AliFMD *FMD  = new AliFMDv0("FMD","normal FMD");
  }

  if(iMUON) {
    //=================== MUON parameters ===========================
    AliMUON *MUON  = new AliMUONv1("MUON","default");
  }

  //=================== PHOS parameters ===========================

  if(iPHOS) {
    AliPHOS *PHOS  = new AliPHOSv1("PHOS","GPS2");
  }

  if(iPMD) {
    //=================== PMD parameters ============================

    AliPMD *PMD  = new AliPMDv1("PMD","normal PMD");
    PMD->SetPAR(1., 1., 0.8, 0.02);
    PMD->SetIN(6., 18., -580., 27., 27.);
    PMD->SetGEO(0.0, 0.2, 4.);
    PMD->SetPadSize(0.8, 1.0, 1.0, 1.5);

  }

  if(iSTART) {
    //=================== START parameters ============================
    AliSTART *START  = new AliSTARTv1("START","START Detector");
  }

}
<commit_msg>ConfigPPR.C removed to avoid confusions with Config.C. Config.C contains all the information for PPR<commit_after><|endoftext|>


<commit_before>
#include <iostream>
#include <fmt/ostream.h>
#include <vector>
#include <sstream>

#include <xl/templates.h>
#include <xl/library_extensions.h>
using xl::templates::ProviderPtr;

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
#include "clang/AST/DeclCXX.h"
#pragma clang diagnostic pop

#include "../wrapped_class.h"
#include "../parsed_method.h"
#include "bidirectional_output.h"
#include "../helper_functions.h"

//MAY BE OTHER ISSUES WITH CONSTRUCTOR PARAMETERS

namespace v8toolkit::class_parser::bidirectional_output {

using namespace xl::templates;

extern xl::templates::Template class_template;
extern std::map<string, xl::templates::Template> bidirectional_templates;

bool BidirectionalCriteria::operator()(WrappedClass const & c) {
    if (!c.bidirectional) {
        log.info(LogSubjects::Subjects::BidirectionalOutput,
                 "BidirectionalCriteria: skipping {} because not bidirectional", c.class_name);
        return false;
    }

    if (c.base_types.size() != 1) {
        log.error(LogSubjects::Subjects::BidirectionalOutput,
                  "BidirectionalCriteria: bidirectional class {} must have 1 base type but actually has {} - {}",
                  c.get_js_name(), c.base_types.size(), xl::join(c.base_types));
        return false;
    }

    return true;
}

std::ostream & BidirectionalOutputStreamProvider::get_class_collection_stream() {
    return std::cerr;
}

ostream & BidirectionalOutputStreamProvider::get_class_stream(WrappedClass const & c) {
    this->output_file.close();
    if (auto results = xl::RegexPcre("^\"([^\"]+)\"$").match(c.my_include)) {
        this->output_file.open(results[1]);
        return this->output_file;
    } else {
        llvm::report_fatal_error("bidirectional type my_include field not in expected format");
    }
}

std::string generate_bidirectional_constructor_parameter_list(WrappedClass const & c) {
    auto base_type = *c.base_types.begin();
    ClassFunction bidirectional_constructor(*base_type, base_type->bidirectional_constructor);
    int param_position = 1;
    std::string result;
    result.reserve(64);
    for (auto & parameter : bidirectional_constructor.parameters) {
        result += fmt::format(", {} var{}", parameter.type.get_name(), param_position++);
    }
    return result;
}

vector<string> generate_variable_names(vector<QualType> qual_types, bool with_std_move) {
    vector<string> results;
    for (std::size_t i = 0; i < qual_types.size(); i++) {
        if (with_std_move && qual_types[i]->isRValueReferenceType()) {
            results.push_back(fmt::format("std::move(var{})", i + 1));
        } else {
            results.push_back(fmt::format("var{}", i + 1));
        }
    }
    return results;
}

vector<QualType> get_method_param_qual_types(const CXXMethodDecl * method,
                                             string const & annotation = "") {
    vector<QualType> results;
    auto parameter_count = method->getNumParams();
    for (unsigned int i = 0; i < parameter_count; i++) {
        auto param_decl = method->getParamDecl(i);

        Annotations annotations(param_decl);
        if (annotation != "" && !annotations.has(annotation)) {
            continue;
        }
        auto param_qual_type = param_decl->getType();
        results.push_back(param_qual_type);
    }
    return results;
}

struct BidirectionalProviderContainer {

    static ProviderPtr get_provider(WrappedClass const & c) {

        std::vector<MemberFunction const *> virtual_functions;
        c.foreach_inheritance_level([&](auto & current) {
            for(auto & f : current.get_member_functions()) {
                std::cerr << fmt::format("in {}, {} is virtual override? {}", current.class_name, f->name, f->is_virtual_override) << std::endl;
                if (f->is_virtual && !f->is_virtual_override) {
                    std::cerr << fmt::format("adding it") << std::endl;
                    virtual_functions.push_back(f.get());
                }
            }
        });

//
        log.info(LogSubjects::BidirectionalOutput, "virtual function count for {}: {}", c.get_js_name(), virtual_functions.size());

        log.info(LogSubjects::BidirectionalOutput, "includes for {}, {}", c.get_js_name(), xl::join(c.include_files));

        return xl::templates::make_provider<BidirectionalProviderContainer>(
            std::pair("name", c.get_js_name()),
//            std::pair("virtual_functions", xl::erase_if(xl::copy(c.get_member_functions()), [](auto & e){return e.get()->is_virtual;})),
            std::pair("virtual_functions", virtual_functions),
            std::pair("includes", std::ref(c.include_files)),
            std::pair("base_name", (*c.base_types.begin())->class_name),
            std::pair("constructor_parameters", generate_bidirectional_constructor_parameter_list(c)),
            std::pair("constructor_variables",
                      xl::join(generate_variable_names(get_method_param_qual_types((*c.base_types.begin())->bidirectional_constructor), true)))
        );
    }

    static ProviderPtr get_provider(MemberFunction const & f) {
        return xl::templates::make_provider<BidirectionalProviderContainer>(
            std::pair("name", f.get_short_name()),
            std::pair("js_name", f.js_name),
            std::pair("comment", f.comment),
            std::pair("params", f.parameters),
            std::pair("return_type", make_macro_safe_comma(f.return_type.get_name())),
            std::pair("param_count", std::to_string(f.parameters.size())),
            std::pair("const", f.is_const() ? "_CONST" : "")
        );
    }

    static ProviderPtr get_provider(ClassFunction::ParameterInfo const & p) {
        return xl::templates::make_provider<BidirectionalProviderContainer>(
            std::pair("type", make_macro_safe_comma(p.type.get_name())),
            std::pair("name", p.name)
        );
    }

    static ProviderPtr get_provider(TypeInfo const & t) {
        return xl::templates::make_provider<BidirectionalProviderContainer>("Implement me");
    }
};

void BidirectionalOutputModule::process(std::vector < WrappedClass const*> wrapped_classes)
{

    BidirectionalOutputStreamProvider stream_provider;

    log.info(LogSubjects::Subjects::BidirectionalOutput, "Starting Bidirectional output module");

    log.info(LogT::Subjects::BidirectionalOutput, "Bidirectional wrapped classes count: {}",
             wrapped_classes.size());

    for(auto c : wrapped_classes) {
        log.info(LogT::Subjects::BidirectionalOutput, "Creating bidirectional output for class: {}",
                 c->class_name);
        auto & ostream = this->output_stream_provider->get_class_stream(*c);

        ostream << bidirectional_templates["class"].template fill<BidirectionalProviderContainer>(std::ref(*c), &bidirectional_templates);
    }

    log.info(LogSubjects::Subjects::BidirectionalOutput, "Finished Bidirectional output module");

}

string BidirectionalOutputModule::get_name() {
    return "BidirectionalOutputModule";
}

OutputCriteria & BidirectionalOutputModule::get_criteria() {
    return this->criteria;
}

::xl::templates::Template bidirectional_class_template(R"(#pragma once

{{includes|!!
#include {{include}}}}

class {{name}} : public {{base_name}}, public v8toolkit::JSWrapper<{{base_name}}> {
public:

    {{name}}(v8::Local<v8::Context> context, v8::Local<v8::Object> object,
        v8::Local<v8::FunctionTemplate> created_by{{constructor_parameters}}) :
      {{base_name}}({{constructor_variables}}),
      v8toolkit::JSWrapper<{{base_name}}>(context, object, created_by)
    {}

{{virtual_functions|!!
    JS_ACCESS_{{param_count}}{{const}}({{return_type}}, {{<name}}, {{js_name}}{{params%%, |!{{type}}}});}}
};)");

std::map<string, Template> bidirectional_templates {
    std::pair("class", bidirectional_class_template),
};

} // end namespace v8toolkit::class_parser::bidirectional_output<commit_msg>fixed bug generating stub/js template with duplicate virtuals when overridden in derived classes<commit_after>
#include <iostream>
#include <fmt/ostream.h>
#include <vector>
#include <sstream>

#include <xl/templates.h>
#include <xl/library_extensions.h>
using xl::templates::ProviderPtr;

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
#include "clang/AST/DeclCXX.h"
#pragma clang diagnostic pop

#include "../wrapped_class.h"
#include "../parsed_method.h"
#include "bidirectional_output.h"
#include "../helper_functions.h"

//MAY BE OTHER ISSUES WITH CONSTRUCTOR PARAMETERS

namespace v8toolkit::class_parser::bidirectional_output {

using namespace xl::templates;

extern xl::templates::Template class_template;
extern std::map<string, xl::templates::Template> bidirectional_templates;

bool BidirectionalCriteria::operator()(WrappedClass const & c) {
    if (!c.bidirectional) {
        log.info(LogSubjects::Subjects::BidirectionalOutput,
                 "BidirectionalCriteria: skipping {} because not bidirectional", c.class_name);
        return false;
    }

    if (c.base_types.size() != 1) {
        log.error(LogSubjects::Subjects::BidirectionalOutput,
                  "BidirectionalCriteria: bidirectional class {} must have 1 base type but actually has {} - {}",
                  c.get_js_name(), c.base_types.size(), xl::join(c.base_types));
        return false;
    }

    return true;
}

std::ostream & BidirectionalOutputStreamProvider::get_class_collection_stream() {
    return std::cerr;
}

ostream & BidirectionalOutputStreamProvider::get_class_stream(WrappedClass const & c) {
    this->output_file.close();
    if (auto results = xl::RegexPcre("^\"([^\"]+)\"$").match(c.my_include)) {
        this->output_file.open(results[1]);
        return this->output_file;
    } else {
        llvm::report_fatal_error("bidirectional type my_include field not in expected format");
    }
}

std::string generate_bidirectional_constructor_parameter_list(WrappedClass const & c) {
    auto base_type = *c.base_types.begin();
    ClassFunction bidirectional_constructor(*base_type, base_type->bidirectional_constructor);
    int param_position = 1;
    std::string result;
    result.reserve(64);
    for (auto & parameter : bidirectional_constructor.parameters) {
        result += fmt::format(", {} var{}", parameter.type.get_name(), param_position++);
    }
    return result;
}

vector<string> generate_variable_names(vector<QualType> qual_types, bool with_std_move) {
    vector<string> results;
    for (std::size_t i = 0; i < qual_types.size(); i++) {
        if (with_std_move && qual_types[i]->isRValueReferenceType()) {
            results.push_back(fmt::format("std::move(var{})", i + 1));
        } else {
            results.push_back(fmt::format("var{}", i + 1));
        }
    }
    return results;
}

vector<QualType> get_method_param_qual_types(const CXXMethodDecl * method,
                                             string const & annotation = "") {
    vector<QualType> results;
    auto parameter_count = method->getNumParams();
    for (unsigned int i = 0; i < parameter_count; i++) {
        auto param_decl = method->getParamDecl(i);

        Annotations annotations(param_decl);
        if (annotation != "" && !annotations.has(annotation)) {
            continue;
        }
        auto param_qual_type = param_decl->getType();
        results.push_back(param_qual_type);
    }
    return results;
}

struct BidirectionalProviderContainer {

    static ProviderPtr get_provider(WrappedClass const & c) {

        std::vector<MemberFunction const *> virtual_functions;
        c.foreach_inheritance_level([&](auto & current) {
            for(auto & f : current.get_member_functions()) {
                if (f->is_virtual && !f->is_virtual_override) {
                    virtual_functions.push_back(f.get());
                }
            }
        });

//
        log.info(LogSubjects::BidirectionalOutput, "virtual function count for {}: {}", c.get_js_name(), virtual_functions.size());

        log.info(LogSubjects::BidirectionalOutput, "includes for {}, {}", c.get_js_name(), xl::join(c.include_files));

        return xl::templates::make_provider<BidirectionalProviderContainer>(
            std::pair("name", c.get_js_name()),
//            std::pair("virtual_functions", xl::erase_if(xl::copy(c.get_member_functions()), [](auto & e){return e.get()->is_virtual;})),
            std::pair("virtual_functions", virtual_functions),
            std::pair("includes", std::ref(c.include_files)),
            std::pair("base_name", (*c.base_types.begin())->class_name),
            std::pair("constructor_parameters", generate_bidirectional_constructor_parameter_list(c)),
            std::pair("constructor_variables",
                      xl::join(generate_variable_names(get_method_param_qual_types((*c.base_types.begin())->bidirectional_constructor), true)))
        );
    }

    static ProviderPtr get_provider(MemberFunction const & f) {
        return xl::templates::make_provider<BidirectionalProviderContainer>(
            std::pair("name", f.get_short_name()),
            std::pair("js_name", f.js_name),
            std::pair("comment", f.comment),
            std::pair("params", f.parameters),
            std::pair("return_type", make_macro_safe_comma(f.return_type.get_name())),
            std::pair("param_count", std::to_string(f.parameters.size())),
            std::pair("const", f.is_const() ? "_CONST" : "")
        );
    }

    static ProviderPtr get_provider(ClassFunction::ParameterInfo const & p) {
        return xl::templates::make_provider<BidirectionalProviderContainer>(
            std::pair("type", make_macro_safe_comma(p.type.get_name())),
            std::pair("name", p.name)
        );
    }

    static ProviderPtr get_provider(TypeInfo const & t) {
        return xl::templates::make_provider<BidirectionalProviderContainer>("Implement me");
    }
};

void BidirectionalOutputModule::process(std::vector < WrappedClass const*> wrapped_classes)
{

    BidirectionalOutputStreamProvider stream_provider;

    log.info(LogSubjects::Subjects::BidirectionalOutput, "Starting Bidirectional output module");

    log.info(LogT::Subjects::BidirectionalOutput, "Bidirectional wrapped classes count: {}",
             wrapped_classes.size());

    for(auto c : wrapped_classes) {
        log.info(LogT::Subjects::BidirectionalOutput, "Creating bidirectional output for class: {}",
                 c->class_name);
        auto & ostream = this->output_stream_provider->get_class_stream(*c);

        ostream << bidirectional_templates["class"].template fill<BidirectionalProviderContainer>(std::ref(*c), &bidirectional_templates);
    }

    log.info(LogSubjects::Subjects::BidirectionalOutput, "Finished Bidirectional output module");

}

string BidirectionalOutputModule::get_name() {
    return "BidirectionalOutputModule";
}

OutputCriteria & BidirectionalOutputModule::get_criteria() {
    return this->criteria;
}

::xl::templates::Template bidirectional_class_template(R"(#pragma once

{{includes|!!
#include {{include}}}}

class {{name}} : public {{base_name}}, public v8toolkit::JSWrapper<{{base_name}}> {
public:

    {{name}}(v8::Local<v8::Context> context, v8::Local<v8::Object> object,
        v8::Local<v8::FunctionTemplate> created_by{{constructor_parameters}}) :
      {{base_name}}({{constructor_variables}}),
      v8toolkit::JSWrapper<{{base_name}}>(context, object, created_by)
    {}

{{virtual_functions|!!
    JS_ACCESS_{{param_count}}{{const}}({{return_type}}, {{<name}}, {{js_name}}{{params%%, |!{{type}}}});}}
};)");

std::map<string, Template> bidirectional_templates {
    std::pair("class", bidirectional_class_template),
};

} // end namespace v8toolkit::class_parser::bidirectional_output<|endoftext|>


<commit_before>/*
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "propertywidgetcontainer.h"
#include "propertywidgetitem.h"
#include "core/gluonobject.h"

#include <QtGui/QGridLayout>
#include <QtGui/QBoxLayout>
#include <QtGui/QToolButton>
#include <QtGui/QCheckBox>
#include <QtGui/QLabel>
#include <QtGui/QMenu>
#include <QtCore/QHash>
#include <QtCore/QMetaProperty>
#include <QtCore/QStringBuilder>
#include <KIcon>

namespace GluonCreator {
class PropertyWidgetContainer::PropertyWidgetContainerPrivate
{
    public:
        PropertyWidgetContainerPrivate(PropertyWidgetContainer* parent)
            : expanded(true)
            , enabled(true)
            , containerWidget(0)
            , containerLayout(0)
            , expander(0)
            , enabler(0)
            , menuButton(0)
        {
            this->parent = parent;
            menu = new QMenu(parent);

            // The widget used for the heading
            QFrame* titleWidget = new QFrame(parent);
            titleWidget->setFrameShape(QFrame::StyledPanel);
            titleWidget->setFrameShadow(QFrame::Raised);
            parent->layout()->addWidget(titleWidget);

            QHBoxLayout* titleLayout = new QHBoxLayout();
            titleLayout->setSpacing(0);
            titleLayout->setContentsMargins(0, 0, 0, 0);
            titleWidget->setLayout(titleLayout);

            expander = new QToolButton(titleWidget);
            expander->setAutoRaise(true);
            expander->setArrowType(Qt::DownArrow);
            connect(expander, SIGNAL(clicked(bool)), parent, SLOT(toggleExpanded()));
            titleLayout->addWidget(expander);

            enabler = new QCheckBox(titleWidget);
            enabler->setText(title);
            enabler->setChecked(enabled);
            connect(enabler, SIGNAL(toggled(bool)), parent, SLOT(setEnabled(bool)));
            titleLayout->addWidget(enabler);

            menuButton = new QToolButton(titleWidget);
            menuButton->setIcon(KIcon("preferences-other"));
            menuButton->setAutoRaise(true);
            menuButton->setPopupMode(QToolButton::InstantPopup);
            menuButton->setMenu(menu);
            titleLayout->addWidget(menuButton);

            // The widget used for containing the children
            containerWidget = new QWidget(parent);
            containerLayout = new QGridLayout();
            containerLayout->setSpacing(0);
            containerLayout->setContentsMargins(0, 0, 0, 0);
            containerWidget->setLayout(containerLayout);
            parent->layout()->addWidget(containerWidget);
        }
        ~PropertyWidgetContainerPrivate()
        {
        }

        QString humanifyString(QString fixThis);

        PropertyWidgetContainer* parent;
        GluonCore::GluonObject* object;
        void appendMetaObject(QObject* object);
        void addPropertyItem(QString name, PropertyWidgetItem* item);

        bool expanded;
        bool enabled;
        QString title;

        QWidget* containerWidget;
        QGridLayout* containerLayout;
        QHash<QString, QWidget*> items;

        QToolButton* expander;
        QCheckBox* enabler;
        QToolButton* menuButton;

        QMenu* menu;
}; }

using namespace GluonCreator;

PropertyWidgetContainer::PropertyWidgetContainer(GluonCore::GluonObject* theObject, QWidget* parent, Qt::WindowFlags f)
    : QWidget(parent, f)
{
    // Create a base layout to add our two children to...
    QVBoxLayout* layout = new QVBoxLayout(this);
    layout->setSpacing(0);
    layout->setContentsMargins(0, 0, 0, 0);
    setLayout(layout);

    d = new PropertyWidgetContainerPrivate(this);
    setObject(theObject);
}

PropertyWidgetContainer::~PropertyWidgetContainer()
{
    delete(d);
}

void PropertyWidgetContainer::setObject(GluonCore::GluonObject* theObject)
{
    d->object = theObject;

    QString classname(theObject->metaObject()->className());
    classname = classname.right(classname.length() - classname.lastIndexOf(':') - 1);
    setTitle(d->humanifyString(classname));

    if(!theObject->property("enabled").isNull())
        setEnabled(theObject->property("enabled").value<bool>());
    if(!theObject->property("expanded").isNull())
        setExpanded(theObject->property("expanded").value<bool>());
    connect(this, SIGNAL(propertyChanged(QObject*,QString,QVariant,QVariant)), parentWidget(), SIGNAL(propertyChanged(QObject*,QString,QVariant,QVariant)));

    PropertyWidgetItem *nameWidget = PropertyWidgetItemFactory::instance()->create(theObject, "QString", parentWidget());
    nameWidget->setEditObject(theObject);
    nameWidget->setEditProperty("name");
    d->addPropertyItem("name", nameWidget);

    PropertyWidgetItem *descriptionWidget = PropertyWidgetItemFactory::instance()->create(theObject, "QString", parentWidget());
    descriptionWidget->setEditObject(theObject);
    descriptionWidget->setEditProperty("description");
    d->addPropertyItem("description", descriptionWidget);

    QFrame* separator = new QFrame(this);
    separator->setFrameShape(QFrame::HLine);
    d->containerLayout->addWidget(separator, d->containerLayout->rowCount(), 0, 1, 2);

    d->appendMetaObject(theObject);
}

bool
PropertyWidgetContainer::expanded() const
{
    return d->expanded;
}

void
PropertyWidgetContainer::setExpanded(const bool& newExpanded)
{
    bool oldExpanded = d->expanded;
    d->expanded = newExpanded;

    // This is done to avoid polluting the GDL with unneeded data - the invalid QVariant removes the expanded property, because true is the default value
    if(!newExpanded)
        d->object->setProperty("expanded", false);
    else
        d->object->setProperty("expanded", QVariant());

    if(newExpanded)
    {
        d->expander->setArrowType(Qt::DownArrow);
    }
    else
    {
        d->expander->setArrowType(Qt::RightArrow);
    }
    // This should be animated - need to experiment with the animation framework for that to be pretty ;)
    d->containerWidget->setVisible(newExpanded);

    emit propertyChanged(d->object, "expanded", oldExpanded, newExpanded);
}

void
PropertyWidgetContainer::toggleExpanded()
{
    setExpanded(!expanded());
}

bool
PropertyWidgetContainer::enabled() const
{
    return d->enabled;
}

void
PropertyWidgetContainer::setEnabled(const bool& newEnabled)
{
    bool oldEnabled = d->enabled;
    d->enabled = newEnabled;
    if(d->enabled != d->object->property("enabled").value<bool>())
        d->object->setProperty("enabled", newEnabled);
    if(d->enabler->isChecked() != newEnabled)
        d->enabler->setChecked(newEnabled);
    emit propertyChanged(d->object, "expanded", oldEnabled, newEnabled);
}

QString
PropertyWidgetContainer::title() const
{
    return d->title;
}

void
PropertyWidgetContainer::setTitle(const QString& newTitle)
{
    d->title = newTitle;
    d->enabler->setText(newTitle);
}

void
PropertyWidgetContainer::addDefaultMenuItems()
{
}

void
PropertyWidgetContainer::addMenuItem(QAction* menuItem)
{
    d->menu->addAction(menuItem);
}

void
PropertyWidgetContainer::removeMenuItem(QAction* menuItem)
{
    if(d->menu->actions().contains(menuItem))
        d->menu->removeAction(menuItem);
}

void
PropertyWidgetContainer::PropertyWidgetContainerPrivate::appendMetaObject(QObject* object)
{
    QString propertyName, propertyDescription;
    QVariant propertyValue;

    const QMetaObject *metaObject = object->metaObject();
    QMetaProperty metaProperty;

    int count = metaObject->propertyCount();
    for (int i = 0; i < count; ++i)
    {
        metaProperty = metaObject->property(i);

        // Guard against adding these, as we manually add them above, to ensure that they
        // are right at the top and have a little line under them and such... all pretty ;)
        // Also disregard enabled, as that is done by the checkbox above...
        propertyName = QString(metaProperty.name());
        if (propertyName == QString("objectName") || propertyName == QString("name") || propertyName == QString("description") || propertyName == QString("expanded") || propertyName == QString("enabled"))
            continue;

        PropertyWidgetItem *editWidget = PropertyWidgetItemFactory::instance()->create(object, metaProperty.typeName(), parent->parentWidget());
        editWidget->setEditObject(object);
        editWidget->setEditProperty(metaProperty.name());

        addPropertyItem(metaProperty.name(), editWidget);
    }

    foreach(const QByteArray &propName, object->dynamicPropertyNames())
    {
        QString thePropName(propName);
        // Don't show the expanded dynamic property - we make this one dynamic because
        // we don't want to pollute the API with this arguably very meta information
        if(thePropName == QString("expanded") || thePropName == QString("enabled"))
            continue;

        PropertyWidgetItem *editWidget = PropertyWidgetItemFactory::instance()->create(object, object->property(propName).typeName(), parent->parentWidget());
        editWidget->setEditObject(object);
        editWidget->setEditProperty(thePropName);

        addPropertyItem(thePropName, editWidget);
    }
}

void
PropertyWidgetContainer::PropertyWidgetContainerPrivate::addPropertyItem(QString name, PropertyWidgetItem* item)
{

    QLabel* nameLabel = new QLabel(parent);
    nameLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
    // Sanitize the name according to the camelCasing
    // - Make first letter upper case
    // - Split at upper-case letters
    // - Add space between those indicated words
    nameLabel->setText(humanifyString(name));

    item->setMinimumWidth(250);
    item->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
    // Yeah, looks a bit funny, but this makes it possible to connect to either the pwi container... or the pwi view ;)
    connect(item, SIGNAL(propertyChanged(QObject*, QString, QVariant, QVariant)), parent, SIGNAL(propertyChanged(QObject*, QString, QVariant, QVariant)));

    int row = containerLayout->rowCount();
    containerLayout->addWidget(nameLabel, row, 0);
    containerLayout->addWidget(item, row, 1);

    items.insert(name, item);
}

QString
PropertyWidgetContainer::PropertyWidgetContainerPrivate::humanifyString(QString fixThis)
{
    QString fixedString;
    const int length = fixThis.size();
    for(int i = 0; i < length; ++i)
    {
        const QChar current = fixThis.at(i);
        if(i == 0)
        {
            // Always upper-case the first word, whether it is or not...
            fixedString = current.toUpper();
        }
        else
        {
            if(current.isUpper())
            {
                fixedString = fixedString % ' ' % current;
            }
            else
            {
                fixedString = fixedString % current;
            }
        }
    }
    return fixedString;
}

#include "propertywidgetcontainer.moc"
<commit_msg>Right, description is now hidden when dynamic as well (it is a dynamic property in Assets, and indeed all other Gluonobjects)<commit_after>/*
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "propertywidgetcontainer.h"
#include "propertywidgetitem.h"
#include "core/gluonobject.h"

#include <QtGui/QGridLayout>
#include <QtGui/QBoxLayout>
#include <QtGui/QToolButton>
#include <QtGui/QCheckBox>
#include <QtGui/QLabel>
#include <QtGui/QMenu>
#include <QtCore/QHash>
#include <QtCore/QMetaProperty>
#include <QtCore/QStringBuilder>
#include <KIcon>

namespace GluonCreator {
class PropertyWidgetContainer::PropertyWidgetContainerPrivate
{
    public:
        PropertyWidgetContainerPrivate(PropertyWidgetContainer* parent)
            : expanded(true)
            , enabled(true)
            , containerWidget(0)
            , containerLayout(0)
            , expander(0)
            , enabler(0)
            , menuButton(0)
        {
            this->parent = parent;
            menu = new QMenu(parent);

            // The widget used for the heading
            QFrame* titleWidget = new QFrame(parent);
            titleWidget->setFrameShape(QFrame::StyledPanel);
            titleWidget->setFrameShadow(QFrame::Raised);
            parent->layout()->addWidget(titleWidget);

            QHBoxLayout* titleLayout = new QHBoxLayout();
            titleLayout->setSpacing(0);
            titleLayout->setContentsMargins(0, 0, 0, 0);
            titleWidget->setLayout(titleLayout);

            expander = new QToolButton(titleWidget);
            expander->setAutoRaise(true);
            expander->setArrowType(Qt::DownArrow);
            connect(expander, SIGNAL(clicked(bool)), parent, SLOT(toggleExpanded()));
            titleLayout->addWidget(expander);

            enabler = new QCheckBox(titleWidget);
            enabler->setText(title);
            enabler->setChecked(enabled);
            connect(enabler, SIGNAL(toggled(bool)), parent, SLOT(setEnabled(bool)));
            titleLayout->addWidget(enabler);

            menuButton = new QToolButton(titleWidget);
            menuButton->setIcon(KIcon("preferences-other"));
            menuButton->setAutoRaise(true);
            menuButton->setPopupMode(QToolButton::InstantPopup);
            menuButton->setMenu(menu);
            titleLayout->addWidget(menuButton);

            // The widget used for containing the children
            containerWidget = new QWidget(parent);
            containerLayout = new QGridLayout();
            containerLayout->setSpacing(0);
            containerLayout->setContentsMargins(0, 0, 0, 0);
            containerWidget->setLayout(containerLayout);
            parent->layout()->addWidget(containerWidget);
        }
        ~PropertyWidgetContainerPrivate()
        {
        }

        QString humanifyString(QString fixThis);

        PropertyWidgetContainer* parent;
        GluonCore::GluonObject* object;
        void appendMetaObject(QObject* object);
        void addPropertyItem(QString name, PropertyWidgetItem* item);

        bool expanded;
        bool enabled;
        QString title;

        QWidget* containerWidget;
        QGridLayout* containerLayout;
        QHash<QString, QWidget*> items;

        QToolButton* expander;
        QCheckBox* enabler;
        QToolButton* menuButton;

        QMenu* menu;
}; }

using namespace GluonCreator;

PropertyWidgetContainer::PropertyWidgetContainer(GluonCore::GluonObject* theObject, QWidget* parent, Qt::WindowFlags f)
    : QWidget(parent, f)
{
    // Create a base layout to add our two children to...
    QVBoxLayout* layout = new QVBoxLayout(this);
    layout->setSpacing(0);
    layout->setContentsMargins(0, 0, 0, 0);
    setLayout(layout);

    d = new PropertyWidgetContainerPrivate(this);
    setObject(theObject);
}

PropertyWidgetContainer::~PropertyWidgetContainer()
{
    delete(d);
}

void PropertyWidgetContainer::setObject(GluonCore::GluonObject* theObject)
{
    d->object = theObject;

    QString classname(theObject->metaObject()->className());
    classname = classname.right(classname.length() - classname.lastIndexOf(':') - 1);
    setTitle(d->humanifyString(classname));

    if(!theObject->property("enabled").isNull())
        setEnabled(theObject->property("enabled").value<bool>());
    if(!theObject->property("expanded").isNull())
        setExpanded(theObject->property("expanded").value<bool>());
    connect(this, SIGNAL(propertyChanged(QObject*,QString,QVariant,QVariant)), parentWidget(), SIGNAL(propertyChanged(QObject*,QString,QVariant,QVariant)));

    PropertyWidgetItem *nameWidget = PropertyWidgetItemFactory::instance()->create(theObject, "QString", parentWidget());
    nameWidget->setEditObject(theObject);
    nameWidget->setEditProperty("name");
    d->addPropertyItem("name", nameWidget);

    PropertyWidgetItem *descriptionWidget = PropertyWidgetItemFactory::instance()->create(theObject, "QString", parentWidget());
    descriptionWidget->setEditObject(theObject);
    descriptionWidget->setEditProperty("description");
    d->addPropertyItem("description", descriptionWidget);

    QFrame* separator = new QFrame(this);
    separator->setFrameShape(QFrame::HLine);
    d->containerLayout->addWidget(separator, d->containerLayout->rowCount(), 0, 1, 2);

    d->appendMetaObject(theObject);
}

bool
PropertyWidgetContainer::expanded() const
{
    return d->expanded;
}

void
PropertyWidgetContainer::setExpanded(const bool& newExpanded)
{
    bool oldExpanded = d->expanded;
    d->expanded = newExpanded;

    // This is done to avoid polluting the GDL with unneeded data - the invalid QVariant removes the expanded property, because true is the default value
    if(!newExpanded)
        d->object->setProperty("expanded", false);
    else
        d->object->setProperty("expanded", QVariant());

    if(newExpanded)
    {
        d->expander->setArrowType(Qt::DownArrow);
    }
    else
    {
        d->expander->setArrowType(Qt::RightArrow);
    }
    // This should be animated - need to experiment with the animation framework for that to be pretty ;)
    d->containerWidget->setVisible(newExpanded);

    emit propertyChanged(d->object, "expanded", oldExpanded, newExpanded);
}

void
PropertyWidgetContainer::toggleExpanded()
{
    setExpanded(!expanded());
}

bool
PropertyWidgetContainer::enabled() const
{
    return d->enabled;
}

void
PropertyWidgetContainer::setEnabled(const bool& newEnabled)
{
    bool oldEnabled = d->enabled;
    d->enabled = newEnabled;
    if(d->enabled != d->object->property("enabled").value<bool>())
        d->object->setProperty("enabled", newEnabled);
    if(d->enabler->isChecked() != newEnabled)
        d->enabler->setChecked(newEnabled);
    emit propertyChanged(d->object, "expanded", oldEnabled, newEnabled);
}

QString
PropertyWidgetContainer::title() const
{
    return d->title;
}

void
PropertyWidgetContainer::setTitle(const QString& newTitle)
{
    d->title = newTitle;
    d->enabler->setText(newTitle);
}

void
PropertyWidgetContainer::addDefaultMenuItems()
{
}

void
PropertyWidgetContainer::addMenuItem(QAction* menuItem)
{
    d->menu->addAction(menuItem);
}

void
PropertyWidgetContainer::removeMenuItem(QAction* menuItem)
{
    if(d->menu->actions().contains(menuItem))
        d->menu->removeAction(menuItem);
}

void
PropertyWidgetContainer::PropertyWidgetContainerPrivate::appendMetaObject(QObject* object)
{
    QString propertyName, propertyDescription;
    QVariant propertyValue;

    const QMetaObject *metaObject = object->metaObject();
    QMetaProperty metaProperty;

    int count = metaObject->propertyCount();
    for (int i = 0; i < count; ++i)
    {
        metaProperty = metaObject->property(i);

        // Guard against adding these, as we manually add them above, to ensure that they
        // are right at the top and have a little line under them and such... all pretty ;)
        // Also disregard enabled, as that is done by the checkbox above...
        propertyName = QString(metaProperty.name());
        if (propertyName == QString("objectName") || propertyName == QString("name") || propertyName == QString("description") || propertyName == QString("expanded") || propertyName == QString("enabled"))
            continue;

        PropertyWidgetItem *editWidget = PropertyWidgetItemFactory::instance()->create(object, metaProperty.typeName(), parent->parentWidget());
        editWidget->setEditObject(object);
        editWidget->setEditProperty(metaProperty.name());

        addPropertyItem(metaProperty.name(), editWidget);
    }

    foreach(const QByteArray &propName, object->dynamicPropertyNames())
    {
        QString thePropName(propName);
        // Don't show the expanded dynamic property - we make this one dynamic because
        // we don't want to pollute the API with this arguably very meta information
        if(thePropName == QString("description") || thePropName == QString("expanded") || thePropName == QString("enabled"))
            continue;

        PropertyWidgetItem *editWidget = PropertyWidgetItemFactory::instance()->create(object, object->property(propName).typeName(), parent->parentWidget());
        editWidget->setEditObject(object);
        editWidget->setEditProperty(thePropName);

        addPropertyItem(thePropName, editWidget);
    }
}

void
PropertyWidgetContainer::PropertyWidgetContainerPrivate::addPropertyItem(QString name, PropertyWidgetItem* item)
{

    QLabel* nameLabel = new QLabel(parent);
    nameLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
    // Sanitize the name according to the camelCasing
    // - Make first letter upper case
    // - Split at upper-case letters
    // - Add space between those indicated words
    nameLabel->setText(humanifyString(name));

    item->setMinimumWidth(250);
    item->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
    // Yeah, looks a bit funny, but this makes it possible to connect to either the pwi container... or the pwi view ;)
    connect(item, SIGNAL(propertyChanged(QObject*, QString, QVariant, QVariant)), parent, SIGNAL(propertyChanged(QObject*, QString, QVariant, QVariant)));

    int row = containerLayout->rowCount();
    containerLayout->addWidget(nameLabel, row, 0);
    containerLayout->addWidget(item, row, 1);

    items.insert(name, item);
}

QString
PropertyWidgetContainer::PropertyWidgetContainerPrivate::humanifyString(QString fixThis)
{
    QString fixedString;
    const int length = fixThis.size();
    for(int i = 0; i < length; ++i)
    {
        const QChar current = fixThis.at(i);
        if(i == 0)
        {
            // Always upper-case the first word, whether it is or not...
            fixedString = current.toUpper();
        }
        else
        {
            if(current.isUpper())
            {
                fixedString = fixedString % ' ' % current;
            }
            else
            {
                fixedString = fixedString % current;
            }
        }
    }
    return fixedString;
}

#include "propertywidgetcontainer.moc"
<|endoftext|>


<commit_before>/*
  DueTimer.cpp - Implementation of Timers defined on DueTimer.h
  For instructions, go to https://github.com/ivanseidel/DueTimer

  Created by Ivan Seidel Gomes, March, 2013.
  Modified by Philipp Klaus, June 2013.
  Thanks to stimmer (from Arduino forum), for coding the "timer soul" (Register stuff)
  Released into the public domain.
*/

#include "DueTimer.h"

const DueTimer::Timer DueTimer::Timers[9] = {
	{TC0,0,TC0_IRQn},
	{TC0,1,TC1_IRQn},
	{TC0,2,TC2_IRQn},
	{TC1,0,TC3_IRQn},
	{TC1,1,TC4_IRQn},
	{TC1,2,TC5_IRQn},
	{TC2,0,TC6_IRQn},
	{TC2,1,TC7_IRQn},
	{TC2,2,TC8_IRQn},
};

void (*DueTimer::callbacks[9])() = {};
double DueTimer::_frequency[9] = {1,1,1,1,1,1,1,1,1};

/*
	Initializing all timers, so you can use them like this: Timer0.start();
*/
DueTimer Timer(0);

DueTimer Timer0(0);
DueTimer Timer1(1);
DueTimer Timer2(2);
DueTimer Timer3(3);
DueTimer Timer4(4);
DueTimer Timer5(5);
DueTimer Timer6(6);
DueTimer Timer7(7);
DueTimer Timer8(8);

DueTimer::DueTimer(int _timer){
	/*
		The constructor of the class DueTimer
	*/

	timer = _timer;
}

DueTimer DueTimer::getAvailable(){
	/*
		Return the first timer with no callback set
	*/

	for(int i = 0; i < 9; i++){
		if(!callbacks[i])
			return DueTimer(i);
	}
	// Default, return Timer0;
	return DueTimer(0);
}

DueTimer DueTimer::attachInterrupt(void (*isr)()){
	/*
		Links the function passed as argument to the timer of the object
	*/

	callbacks[timer] = isr;

	return *this;
}

DueTimer DueTimer::detachInterrupt(){
	/*
		Links the function passed as argument to the timer of the object
	*/

	stop(); // Stop the currently running timer

	callbacks[timer] = NULL;

	return *this;
}

DueTimer DueTimer::start(long microseconds){
	/*
		Start the timer
		If a period is set, then sets the period and start the timer
	*/

	if(microseconds > 0)
		setPeriod(microseconds);

	NVIC_ClearPendingIRQ(Timers[timer].irq);
	NVIC_EnableIRQ(Timers[timer].irq);

	return *this;
}

DueTimer DueTimer::stop(){
	/*
		Stop the timer
	*/

	NVIC_DisableIRQ(Timers[timer].irq);

	return *this;
}

uint8_t DueTimer::bestClock(double frequency, uint32_t& retRC){
	/*
		Pick the best Clock, thanks to Ogle Basil Hall!

		Timer		Definition
		TIMER_CLOCK1	MCK/2
		TIMER_CLOCK2	MCK/8
		TIMER_CLOCK3	MCK/32
		TIMER_CLOCK4	MCK/128
	*/
	struct {
		uint8_t flag;
		uint8_t divisor;
	} clockConfig[] = {
		{ TC_CMR_TCCLKS_TIMER_CLOCK1, 2 },
		{ TC_CMR_TCCLKS_TIMER_CLOCK2, 8 },
		{ TC_CMR_TCCLKS_TIMER_CLOCK3, 32 },
		{ TC_CMR_TCCLKS_TIMER_CLOCK4, 128 }
	};
	float ticks;
	float error;
	int clkId = 3;
	int bestClock = 3;
	float bestError = 1.0;
	do
	{
		ticks = (float) VARIANT_MCK / frequency / (float) clockConfig[clkId].divisor;
		error = abs(ticks - round(ticks));
		if (abs(error) < bestError)
		{
			bestClock = clkId;
			bestError = error;
		}
	} while (clkId-- > 0);
	ticks = (float) VARIANT_MCK / frequency / (float) clockConfig[bestClock].divisor;
	retRC = (uint32_t) round(ticks);
	return clockConfig[bestClock].flag;
}

DueTimer DueTimer::setFrequency(double frequency){
	/*
		Set the timer frequency (in Hz)
	*/

	// Prevent negative frequencies
	if(frequency <= 0) { frequency = 1; }

	// Remember the frequency
	_frequency[timer] = frequency;

	// Get current timer configurations
	Timer t = Timers[timer];

	uint32_t rc = 0;
	uint8_t clock;

	// Tell the Power Management Controller to disable
	// the write protection of the (Timer/Counter) registers:
	pmc_set_writeprotect(false);

	// Enable clock for the timer
	pmc_enable_periph_clk((uint32_t)Timers[timer].irq);

	// Find the best clock for the wanted frequency
	clock = bestClock(frequency, rc);

	// Set up the Timer in waveform mode which creates a PWM
	// in UP mode with automatic trigger on RC Compare
	// and sets it up with the determined internal clock as clock input.
	TC_Configure(t.tc, t.channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | clock);
	// Reset counter and fire interrupt when RC value is matched:
	TC_SetRC(t.tc, t.channel, rc);
	// Start the Counter channel
	TC_Start(t.tc, t.channel);
	// Enable the RC Compare Interrupt...
	t.tc->TC_CHANNEL[t.channel].TC_IER=TC_IER_CPCS;
	// ... and disable all others.
	t.tc->TC_CHANNEL[t.channel].TC_IDR=~TC_IER_CPCS;

	return *this;
}

DueTimer DueTimer::setPeriod(long microseconds){
	/*
		Set the period of the timer (in microseconds)
	*/

	// Convert period in microseconds to frequency in Hz
	double frequency = 1000000.0 / microseconds;
	setFrequency(frequency);
	return *this;
}

double DueTimer::getFrequency(){
	/*
		Get current time frequency
	*/

	return _frequency[timer];
}

long DueTimer::getPeriod(){
	/*
		Get current time period
	*/

	return 1.0/getFrequency()*1000000;
}

/*
	Implementation of the timer callbacks defined in
	arduino-1.5.2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/sam3x8e.h
*/
void TC0_Handler(){
	TC_GetStatus(TC0, 0);
	DueTimer::callbacks[0]();
}
void TC1_Handler(){
	TC_GetStatus(TC0, 1);
	DueTimer::callbacks[1]();
}
void TC2_Handler(){
	TC_GetStatus(TC0, 2);
	DueTimer::callbacks[2]();
}
void TC3_Handler(){
	TC_GetStatus(TC1, 0);
	DueTimer::callbacks[3]();
}
void TC4_Handler(){
	TC_GetStatus(TC1, 1);
	DueTimer::callbacks[4]();
}
void TC5_Handler(){
	TC_GetStatus(TC1, 2);
	DueTimer::callbacks[5]();
}
void TC6_Handler(){
	TC_GetStatus(TC2, 0);
	DueTimer::callbacks[6]();
}
void TC7_Handler(){
	TC_GetStatus(TC2, 1);
	DueTimer::callbacks[7]();
}
void TC8_Handler(){
	TC_GetStatus(TC2, 2);
	DueTimer::callbacks[8]();
}
<commit_msg>fixing bug: check in start() if _frequency is > 0.<commit_after>/*
  DueTimer.cpp - Implementation of Timers defined on DueTimer.h
  For instructions, go to https://github.com/ivanseidel/DueTimer

  Created by Ivan Seidel Gomes, March, 2013.
  Modified by Philipp Klaus, June 2013.
  Thanks to stimmer (from Arduino forum), for coding the "timer soul" (Register stuff)
  Released into the public domain.
*/

#include "DueTimer.h"

const DueTimer::Timer DueTimer::Timers[9] = {
	{TC0,0,TC0_IRQn},
	{TC0,1,TC1_IRQn},
	{TC0,2,TC2_IRQn},
	{TC1,0,TC3_IRQn},
	{TC1,1,TC4_IRQn},
	{TC1,2,TC5_IRQn},
	{TC2,0,TC6_IRQn},
	{TC2,1,TC7_IRQn},
	{TC2,2,TC8_IRQn},
};

void (*DueTimer::callbacks[9])() = {};

/*
	Initializing all timers, so you can use them like this: Timer0.start();
*/
DueTimer Timer(0);

DueTimer Timer0(0);
DueTimer Timer1(1);
DueTimer Timer2(2);
DueTimer Timer3(3);
DueTimer Timer4(4);
DueTimer Timer5(5);
DueTimer Timer6(6);
DueTimer Timer7(7);
DueTimer Timer8(8);

DueTimer::DueTimer(int _timer){
	/*
		The constructor of the class DueTimer
	*/

	timer = _timer;

	_frequency[timer] = -1;
}

DueTimer DueTimer::getAvailable(){
	/*
		Return the first timer with no callback set
	*/

	for(int i = 0; i < 9; i++){
		if(!callbacks[i])
			return DueTimer(i);
	}
	// Default, return Timer0;
	return DueTimer(0);
}

DueTimer DueTimer::attachInterrupt(void (*isr)()){
	/*
		Links the function passed as argument to the timer of the object
	*/

	callbacks[timer] = isr;

	return *this;
}

DueTimer DueTimer::detachInterrupt(){
	/*
		Links the function passed as argument to the timer of the object
	*/

	stop(); // Stop the currently running timer

	callbacks[timer] = NULL;

	return *this;
}

DueTimer DueTimer::start(long microseconds){
	/*
		Start the timer
		If a period is set, then sets the period and start the timer
	*/

	if(microseconds > 0)
		setPeriod(microseconds);

	if(_frequency[timer] <= 0)
		setFrequency(1);

	NVIC_ClearPendingIRQ(Timers[timer].irq);
	NVIC_EnableIRQ(Timers[timer].irq);

	return *this;
}

DueTimer DueTimer::stop(){
	/*
		Stop the timer
	*/

	NVIC_DisableIRQ(Timers[timer].irq);

	return *this;
}

uint8_t DueTimer::bestClock(double frequency, uint32_t& retRC){
	/*
		Pick the best Clock, thanks to Ogle Basil Hall!

		Timer		Definition
		TIMER_CLOCK1	MCK/2
		TIMER_CLOCK2	MCK/8
		TIMER_CLOCK3	MCK/32
		TIMER_CLOCK4	MCK/128
	*/
	struct {
		uint8_t flag;
		uint8_t divisor;
	} clockConfig[] = {
		{ TC_CMR_TCCLKS_TIMER_CLOCK1, 2 },
		{ TC_CMR_TCCLKS_TIMER_CLOCK2, 8 },
		{ TC_CMR_TCCLKS_TIMER_CLOCK3, 32 },
		{ TC_CMR_TCCLKS_TIMER_CLOCK4, 128 }
	};
	float ticks;
	float error;
	int clkId = 3;
	int bestClock = 3;
	float bestError = 1.0;
	do
	{
		ticks = (float) VARIANT_MCK / frequency / (float) clockConfig[clkId].divisor;
		error = abs(ticks - round(ticks));
		if (abs(error) < bestError)
		{
			bestClock = clkId;
			bestError = error;
		}
	} while (clkId-- > 0);
	ticks = (float) VARIANT_MCK / frequency / (float) clockConfig[bestClock].divisor;
	retRC = (uint32_t) round(ticks);
	return clockConfig[bestClock].flag;
}

DueTimer DueTimer::setFrequency(double frequency){
	/*
		Set the timer frequency (in Hz)
	*/

	// Prevent negative frequencies
	if(frequency <= 0) { frequency = 1; }

	// Remember the frequency
	_frequency[timer] = frequency;

	// Get current timer configurations
	Timer t = Timers[timer];

	uint32_t rc = 0;
	uint8_t clock;

	// Tell the Power Management Controller to disable
	// the write protection of the (Timer/Counter) registers:
	pmc_set_writeprotect(false);

	// Enable clock for the timer
	pmc_enable_periph_clk((uint32_t)Timers[timer].irq);

	// Find the best clock for the wanted frequency
	clock = bestClock(frequency, rc);

	// Set up the Timer in waveform mode which creates a PWM
	// in UP mode with automatic trigger on RC Compare
	// and sets it up with the determined internal clock as clock input.
	TC_Configure(t.tc, t.channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | clock);
	// Reset counter and fire interrupt when RC value is matched:
	TC_SetRC(t.tc, t.channel, rc);
	// Start the Counter channel
	TC_Start(t.tc, t.channel);
	// Enable the RC Compare Interrupt...
	t.tc->TC_CHANNEL[t.channel].TC_IER=TC_IER_CPCS;
	// ... and disable all others.
	t.tc->TC_CHANNEL[t.channel].TC_IDR=~TC_IER_CPCS;

	return *this;
}

DueTimer DueTimer::setPeriod(long microseconds){
	/*
		Set the period of the timer (in microseconds)
	*/

	// Convert period in microseconds to frequency in Hz
	double frequency = 1000000.0 / microseconds;
	setFrequency(frequency);
	return *this;
}

double DueTimer::getFrequency(){
	/*
		Get current time frequency
	*/

	return _frequency[timer];
}

long DueTimer::getPeriod(){
	/*
		Get current time period
	*/

	return 1.0/getFrequency()*1000000;
}

/*
	Implementation of the timer callbacks defined in
	arduino-1.5.2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/sam3x8e.h
*/
void TC0_Handler(){
	TC_GetStatus(TC0, 0);
	DueTimer::callbacks[0]();
}
void TC1_Handler(){
	TC_GetStatus(TC0, 1);
	DueTimer::callbacks[1]();
}
void TC2_Handler(){
	TC_GetStatus(TC0, 2);
	DueTimer::callbacks[2]();
}
void TC3_Handler(){
	TC_GetStatus(TC1, 0);
	DueTimer::callbacks[3]();
}
void TC4_Handler(){
	TC_GetStatus(TC1, 1);
	DueTimer::callbacks[4]();
}
void TC5_Handler(){
	TC_GetStatus(TC1, 2);
	DueTimer::callbacks[5]();
}
void TC6_Handler(){
	TC_GetStatus(TC2, 0);
	DueTimer::callbacks[6]();
}
void TC7_Handler(){
	TC_GetStatus(TC2, 1);
	DueTimer::callbacks[7]();
}
void TC8_Handler(){
	TC_GetStatus(TC2, 2);
	DueTimer::callbacks[8]();
}
<|endoftext|>