<commit_before>// Copyright (c) 2010 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/sync/notifier/server_notifier_thread.h"

#include <string>
#include <vector>

#include "base/logging.h"
#include "chrome/browser/sync/notifier/chrome_invalidation_client.h"
#include "googleurl/src/gurl.h"
#include "jingle/notifier/base/notifier_options.h"
#include "jingle/notifier/listener/notification_defines.h"
#include "talk/xmpp/xmppclient.h"
#include "webkit/glue/webkit_glue.h"

namespace sync_notifier {

ServerNotifierThread::ServerNotifierThread(
    const notifier::NotifierOptions& notifier_options,
    const std::string& state, StateWriter* state_writer)
    : notifier::MediatorThreadImpl(notifier_options),
      state_(state),
      state_writers_(new ObserverListThreadSafe<StateWriter>()),
      state_writer_(state_writer) {
  DCHECK_EQ(notifier::NOTIFICATION_SERVER,
            notifier_options.notification_method);
  DCHECK(state_writer_);
  state_writers_->AddObserver(state_writer_);
}

ServerNotifierThread::~ServerNotifierThread() {}

void ServerNotifierThread::ListenForUpdates() {
  DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
  worker_message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &ServerNotifierThread::DoListenForUpdates));
}

void ServerNotifierThread::SubscribeForUpdates(
    const std::vector<std::string>& subscribed_services_list) {
  DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
  worker_message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(
          this, &ServerNotifierThread::RegisterTypesAndSignalSubscribed));
}

void ServerNotifierThread::Logout() {
  DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
  state_writers_->RemoveObserver(state_writer_);
  state_writer_ = NULL;
  worker_message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(this,
                        &ServerNotifierThread::StopInvalidationListener));
  MediatorThreadImpl::Logout();
}

void ServerNotifierThread::SendNotification(
    const OutgoingNotificationData& data) {
  DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
  NOTREACHED() << "Shouldn't send notifications if ServerNotifierThread is "
                  "used";
}

void ServerNotifierThread::OnInvalidate(
    syncable::ModelType model_type,
    const std::string& payload) {
  DCHECK_EQ(MessageLoop::current(), worker_message_loop());
  DCHECK_GE(model_type, syncable::FIRST_REAL_MODEL_TYPE);
  DCHECK_LT(model_type, syncable::MODEL_TYPE_COUNT);
  VLOG(1) << "OnInvalidate: " << syncable::ModelTypeToString(model_type);

  syncable::ModelTypeBitSet model_types;
  model_types[model_type] = true;
  IncomingNotificationData notification_data;
  notification_data.service_url = model_types.to_string();
  notification_data.service_specific_data = payload;
  observers_->Notify(&Observer::OnIncomingNotification, notification_data);
}

void ServerNotifierThread::OnInvalidateAll() {
  DCHECK_EQ(MessageLoop::current(), worker_message_loop());
  VLOG(1) << "OnInvalidateAll";

  syncable::ModelTypeBitSet model_types;
  model_types.set();  // InvalidateAll, so set all datatypes to true.
  IncomingNotificationData notification_data;
  notification_data.service_specific_data = model_types.to_string();
  observers_->Notify(&Observer::OnIncomingNotification, notification_data);
}

void ServerNotifierThread::WriteState(const std::string& state) {
  DCHECK_EQ(MessageLoop::current(), worker_message_loop());
  VLOG(1) << "WriteState";
  state_writers_->Notify(&StateWriter::WriteState, state);
}

void ServerNotifierThread::DoListenForUpdates() {
  DCHECK_EQ(MessageLoop::current(), worker_message_loop());
  if (!base_task_.get()) {
    return;
  }

  if (chrome_invalidation_client_.get()) {
    // If we already have an invalidation client, simply change the
    // base task.
    chrome_invalidation_client_->ChangeBaseTask(base_task_);
  } else {
    // Otherwise, create the invalidation client.
    chrome_invalidation_client_.reset(new ChromeInvalidationClient());

    // TODO(akalin): Make cache_guid() part of the client ID.  If we do
    // so and we somehow propagate it up to the server somehow, we can
    // make it so that we won't receive any notifications that were
    // generated from our own changes.
    const std::string kClientId = "server_notifier_thread";
    // Use user agent as |client_info| so we can use it for debugging
    // server-side.
    const std::string& client_info = webkit_glue::GetUserAgent(GURL());
    chrome_invalidation_client_->Start(
        kClientId, client_info, state_, this, this, base_task_);
    state_.clear();
  }
}

void ServerNotifierThread::RegisterTypesAndSignalSubscribed() {
  DCHECK_EQ(MessageLoop::current(), worker_message_loop());
  if (!chrome_invalidation_client_.get()) {
    return;
  }
  chrome_invalidation_client_->RegisterTypes();
  observers_->Notify(&Observer::OnSubscriptionStateChange, true);
}

void ServerNotifierThread::StopInvalidationListener() {
  DCHECK_EQ(MessageLoop::current(), worker_message_loop());
  chrome_invalidation_client_.reset();
}

}  // namespace sync_notifier
<commit_msg>[SYNC] Fix server notifier's InvalidateAll().<commit_after>// Copyright (c) 2010 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/sync/notifier/server_notifier_thread.h"

#include <string>
#include <vector>

#include "base/logging.h"
#include "chrome/browser/sync/notifier/chrome_invalidation_client.h"
#include "googleurl/src/gurl.h"
#include "jingle/notifier/base/notifier_options.h"
#include "jingle/notifier/listener/notification_defines.h"
#include "talk/xmpp/xmppclient.h"
#include "webkit/glue/webkit_glue.h"

namespace sync_notifier {

ServerNotifierThread::ServerNotifierThread(
    const notifier::NotifierOptions& notifier_options,
    const std::string& state, StateWriter* state_writer)
    : notifier::MediatorThreadImpl(notifier_options),
      state_(state),
      state_writers_(new ObserverListThreadSafe<StateWriter>()),
      state_writer_(state_writer) {
  DCHECK_EQ(notifier::NOTIFICATION_SERVER,
            notifier_options.notification_method);
  DCHECK(state_writer_);
  state_writers_->AddObserver(state_writer_);
}

ServerNotifierThread::~ServerNotifierThread() {}

void ServerNotifierThread::ListenForUpdates() {
  DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
  worker_message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &ServerNotifierThread::DoListenForUpdates));
}

void ServerNotifierThread::SubscribeForUpdates(
    const std::vector<std::string>& subscribed_services_list) {
  DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
  worker_message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(
          this, &ServerNotifierThread::RegisterTypesAndSignalSubscribed));
}

void ServerNotifierThread::Logout() {
  DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
  state_writers_->RemoveObserver(state_writer_);
  state_writer_ = NULL;
  worker_message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(this,
                        &ServerNotifierThread::StopInvalidationListener));
  MediatorThreadImpl::Logout();
}

void ServerNotifierThread::SendNotification(
    const OutgoingNotificationData& data) {
  DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
  NOTREACHED() << "Shouldn't send notifications if ServerNotifierThread is "
                  "used";
}

void ServerNotifierThread::OnInvalidate(
    syncable::ModelType model_type,
    const std::string& payload) {
  DCHECK_EQ(MessageLoop::current(), worker_message_loop());
  DCHECK_GE(model_type, syncable::FIRST_REAL_MODEL_TYPE);
  DCHECK_LT(model_type, syncable::MODEL_TYPE_COUNT);
  VLOG(1) << "OnInvalidate: " << syncable::ModelTypeToString(model_type);

  syncable::ModelTypeBitSet model_types;
  model_types[model_type] = true;
  IncomingNotificationData notification_data;
  notification_data.service_url = model_types.to_string();
  notification_data.service_specific_data = payload;
  observers_->Notify(&Observer::OnIncomingNotification, notification_data);
}

void ServerNotifierThread::OnInvalidateAll() {
  DCHECK_EQ(MessageLoop::current(), worker_message_loop());
  VLOG(1) << "OnInvalidateAll";

  syncable::ModelTypeBitSet model_types;
  model_types.set();  // InvalidateAll, so set all datatypes to true.
  IncomingNotificationData notification_data;
  notification_data.service_url = model_types.to_string();
  notification_data.service_specific_data = std::string();  // No payload.
  observers_->Notify(&Observer::OnIncomingNotification, notification_data);
}

void ServerNotifierThread::WriteState(const std::string& state) {
  DCHECK_EQ(MessageLoop::current(), worker_message_loop());
  VLOG(1) << "WriteState";
  state_writers_->Notify(&StateWriter::WriteState, state);
}

void ServerNotifierThread::DoListenForUpdates() {
  DCHECK_EQ(MessageLoop::current(), worker_message_loop());
  if (!base_task_.get()) {
    return;
  }

  if (chrome_invalidation_client_.get()) {
    // If we already have an invalidation client, simply change the
    // base task.
    chrome_invalidation_client_->ChangeBaseTask(base_task_);
  } else {
    // Otherwise, create the invalidation client.
    chrome_invalidation_client_.reset(new ChromeInvalidationClient());

    // TODO(akalin): Make cache_guid() part of the client ID.  If we do
    // so and we somehow propagate it up to the server somehow, we can
    // make it so that we won't receive any notifications that were
    // generated from our own changes.
    const std::string kClientId = "server_notifier_thread";
    // Use user agent as |client_info| so we can use it for debugging
    // server-side.
    const std::string& client_info = webkit_glue::GetUserAgent(GURL());
    chrome_invalidation_client_->Start(
        kClientId, client_info, state_, this, this, base_task_);
    state_.clear();
  }
}

void ServerNotifierThread::RegisterTypesAndSignalSubscribed() {
  DCHECK_EQ(MessageLoop::current(), worker_message_loop());
  if (!chrome_invalidation_client_.get()) {
    return;
  }
  chrome_invalidation_client_->RegisterTypes();
  observers_->Notify(&Observer::OnSubscriptionStateChange, true);
}

void ServerNotifierThread::StopInvalidationListener() {
  DCHECK_EQ(MessageLoop::current(), worker_message_loop());
  chrome_invalidation_client_.reset();
}

}  // namespace sync_notifier
<|endoftext|>


<commit_before>// Copyright 2015 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 <string>

#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "chrome/browser/devtools/devtools_window_testing.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_iterator.h"
#include "chrome/browser/ui/extensions/app_launch_params.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/browser/ui/extensions/hosted_app_browser_controller.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"

using content::WebContents;
using extensions::Extension;

namespace {

// Used by ShouldLocationBarForXXX. Performs a navigation and then checks that
// the location bar visibility is as expcted.
void NavigateAndCheckForLocationBar(Browser* browser,
                                    const std::string& url_string,
                                    bool expected_visibility) {
  GURL url(url_string);
  ui_test_utils::NavigateToURL(browser, url);
  EXPECT_EQ(expected_visibility,
      browser->hosted_app_controller()->ShouldShowLocationBar());
}

}  // namespace

class HostedAppTest : public ExtensionBrowserTest {
 public:
  HostedAppTest() : app_browser_(nullptr) {}
  ~HostedAppTest() override {}

 protected:
  void SetupApp(const std::string& app_folder, bool is_bookmark_app) {
    const Extension* app = InstallExtensionWithSourceAndFlags(
        test_data_dir_.AppendASCII(app_folder), 1,
        extensions::Manifest::INTERNAL,
        is_bookmark_app ? extensions::Extension::FROM_BOOKMARK
                        : extensions::Extension::NO_FLAGS);
    ASSERT_TRUE(app);

    // Launch it in a window.
    ASSERT_TRUE(OpenApplication(AppLaunchParams(
        browser()->profile(), app, extensions::LAUNCH_CONTAINER_WINDOW,
        NEW_WINDOW, extensions::SOURCE_TEST)));

    for (chrome::BrowserIterator it; !it.done(); it.Next()) {
      if (*it == browser())
        continue;

      std::string browser_app_id =
          web_app::GetExtensionIdFromApplicationName((*it)->app_name());
      if (browser_app_id == app->id()) {
        app_browser_ = *it;
        break;
      }
    }

    ASSERT_TRUE(app_browser_);
    ASSERT_TRUE(app_browser_ != browser());
  }

  Browser* app_browser_;
};

// Check that the location bar is shown correctly for HTTP bookmark apps.
IN_PROC_BROWSER_TEST_F(HostedAppTest,
                       ShouldShowLocationBarForHTTPBookmarkApp) {
  base::CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableNewBookmarkApps);

  SetupApp("app", true);

  // Navigate to the app's launch page; the location bar should be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/empty.html", false);

  // Navigate to another page on the same origin; the location bar should still
  // hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/blah", false);

  // Navigate to the https version of the site; the location bar should
  // be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "https://www.example.com/blah", false);

  // Navigate to different origin; the location bar should now be visible.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.foo.com/blah", true);

  // Navigate back to the app's origin; the location bar should now be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/blah", false);
}

// Check that the location bar is shown correctly for HTTPS bookmark apps.
IN_PROC_BROWSER_TEST_F(HostedAppTest,
                       ShouldShowLocationBarForHTTPSBookmarkApp) {
  base::CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableNewBookmarkApps);

  SetupApp("https_app", true);

  // Navigate to the app's launch page; the location bar should be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "https://www.example.com/empty.html", false);

  // Navigate to another page on the same origin; the location bar should still
  // hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "https://www.example.com/blah", false);

  // Navigate to the http version of the site; the location bar should
  // be visible for the https version as it is now on a less secure version
  // of its host.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/blah", true);

  // Navigate to different origin; the location bar should now be visible.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.foo.com/blah", true);

  // Navigate back to the app's origin; the location bar should now be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "https://www.example.com/blah", false);
}

// Check that the location bar is shown correctly for normal hosted apps.
IN_PROC_BROWSER_TEST_F(HostedAppTest,
                       ShouldShowLocationBarForHostedApp) {
  SetupApp("app", false);

  // Navigate to the app's launch page; the location bar should be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/empty.html", false);

  // Navigate to another page on the same origin; the location bar should still
  // hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/blah", false);

  // Navigate to the https version of the site; the location bar should
  // be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "https://www.example.com/blah", false);

  // Navigate to different origin; the location bar should now be visible.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.foo.com/blah", true);

  // Navigate back to the app's origin; the location bar should now be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/blah", false);
}

// Check that the location bar is shown correctly for hosted apps that specify
// start URLs without the 'www.' prefix.
IN_PROC_BROWSER_TEST_F(HostedAppTest,
                       LocationBarForHostedAppWithoutWWW) {
  SetupApp("app_no_www", false);

  // Navigate to the app's launch page; the location bar should be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://example.com/empty.html", false);

  // Navigate to the app's launch page with the 'www.' prefis; the location bar
  // should be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/empty.html", false);

  // Navigate to different origin; the location bar should now be visible.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.foo.com/blah", true);

  // Navigate back to the app's origin; the location bar should now be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://example.com/blah", false);
}

// Open a normal browser window, a hosted app window, a legacy packaged app
// window and a dev tools window, and check that the web app frame feature is
// supported correctly.
IN_PROC_BROWSER_TEST_F(HostedAppTest, ShouldUseWebAppFrame) {
  base::CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableWebAppFrame);

  // Load a hosted app.
   const Extension* bookmark_app = InstallExtensionWithSourceAndFlags(
      test_data_dir_.AppendASCII("app"),
      1,
      extensions::Manifest::INTERNAL,
      extensions::Extension::FROM_BOOKMARK);
  ASSERT_TRUE(bookmark_app);

  // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
  WebContents* bookmark_app_window = OpenApplication(AppLaunchParams(
      browser()->profile(), bookmark_app, extensions::LAUNCH_CONTAINER_WINDOW,
      NEW_WINDOW, extensions::SOURCE_UNTRACKED));
  ASSERT_TRUE(bookmark_app_window);

  //  Load a packaged app.
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("packaged_app")));
  const Extension* packaged_app = nullptr;
  extensions::ExtensionRegistry* registry =
      extensions::ExtensionRegistry::Get(browser()->profile());
  for (const scoped_refptr<const extensions::Extension>& extension :
       registry->enabled_extensions()) {
    if (extension->name() == "Packaged App Test")
      packaged_app = extension.get();
  }
  ASSERT_TRUE(packaged_app);

  // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
  WebContents* packaged_app_window = OpenApplication(AppLaunchParams(
      browser()->profile(), packaged_app, extensions::LAUNCH_CONTAINER_WINDOW,
      NEW_WINDOW, extensions::SOURCE_UNTRACKED));
  ASSERT_TRUE(packaged_app_window);

  DevToolsWindow* devtools_window =
      DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), false);

  // The launch should have created a new app browser and a dev tools browser.
  ASSERT_EQ(4u, chrome::GetBrowserCount(browser()->profile(),
                                        browser()->host_desktop_type()));

  // Find the new browsers.
  Browser* bookmark_app_browser = nullptr;
  Browser* packaged_app_browser = nullptr;
  Browser* dev_tools_browser = nullptr;
  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
    if (*it == browser()) {
      continue;
    } else if ((*it)->app_name() == DevToolsWindow::kDevToolsApp) {
      dev_tools_browser = *it;
    } else if ((*it)->tab_strip_model()->GetActiveWebContents() ==
               bookmark_app_window) {
      bookmark_app_browser = *it;
    } else {
      packaged_app_browser = *it;
    }
  }
  ASSERT_TRUE(dev_tools_browser);
  ASSERT_TRUE(bookmark_app_browser);
  ASSERT_TRUE(bookmark_app_browser != browser());
  ASSERT_TRUE(packaged_app_browser);
  ASSERT_TRUE(packaged_app_browser != browser());
  ASSERT_TRUE(packaged_app_browser != bookmark_app_browser);

  EXPECT_FALSE(browser()->SupportsWindowFeature(Browser::FEATURE_WEBAPPFRAME));
  EXPECT_FALSE(
      dev_tools_browser->SupportsWindowFeature(Browser::FEATURE_WEBAPPFRAME));
  EXPECT_EQ(browser()->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH,
            bookmark_app_browser->SupportsWindowFeature(
                Browser::FEATURE_WEBAPPFRAME));
  EXPECT_FALSE(packaged_app_browser->SupportsWindowFeature(
      Browser::FEATURE_WEBAPPFRAME));

  DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
}
<commit_msg>Split up and minimise hosted app location bar tests.<commit_after>// Copyright 2015 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 <string>

#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "chrome/browser/devtools/devtools_window_testing.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_iterator.h"
#include "chrome/browser/ui/extensions/app_launch_params.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/browser/ui/extensions/hosted_app_browser_controller.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"

using content::WebContents;
using extensions::Extension;

namespace {

// Used by ShouldLocationBarForXXX. Performs a navigation and then checks that
// the location bar visibility is as expcted.
void NavigateAndCheckForLocationBar(Browser* browser,
                                    const std::string& url_string,
                                    bool expected_visibility) {
  GURL url(url_string);
  ui_test_utils::NavigateToURL(browser, url);
  EXPECT_EQ(expected_visibility,
      browser->hosted_app_controller()->ShouldShowLocationBar());
}

}  // namespace

class HostedAppTest : public ExtensionBrowserTest {
 public:
  HostedAppTest() : app_browser_(nullptr) {}
  ~HostedAppTest() override {}

 protected:
  void SetupApp(const std::string& app_folder, bool is_bookmark_app) {
    const Extension* app = InstallExtensionWithSourceAndFlags(
        test_data_dir_.AppendASCII(app_folder), 1,
        extensions::Manifest::INTERNAL,
        is_bookmark_app ? extensions::Extension::FROM_BOOKMARK
                        : extensions::Extension::NO_FLAGS);
    ASSERT_TRUE(app);

    // Launch it in a window.
    ASSERT_TRUE(OpenApplication(AppLaunchParams(
        browser()->profile(), app, extensions::LAUNCH_CONTAINER_WINDOW,
        NEW_WINDOW, extensions::SOURCE_TEST)));

    for (chrome::BrowserIterator it; !it.done(); it.Next()) {
      if (*it == browser())
        continue;

      std::string browser_app_id =
          web_app::GetExtensionIdFromApplicationName((*it)->app_name());
      if (browser_app_id == app->id()) {
        app_browser_ = *it;
        break;
      }
    }

    ASSERT_TRUE(app_browser_);
    ASSERT_TRUE(app_browser_ != browser());
  }

  Browser* app_browser_;
};

// Check that the location bar is shown correctly for bookmark apps.
IN_PROC_BROWSER_TEST_F(HostedAppTest,
                       ShouldShowLocationBarForBookmarkApp) {
  base::CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableNewBookmarkApps);

  SetupApp("app", true);

  // Navigate to the app's launch page; the location bar should be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/empty.html", false);

  // Navigate to another page on the same origin; the location bar should still
  // hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/blah", false);

  // Navigate to different origin; the location bar should now be visible.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.foo.com/blah", true);
}

// Check that the location bar is shown correctly for HTTP bookmark apps when
// they navigate to a HTTPS page on the same origin.
IN_PROC_BROWSER_TEST_F(HostedAppTest,
                       ShouldShowLocationBarForHTTPBookmarkApp) {
  base::CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableNewBookmarkApps);

  SetupApp("app", true);

  // Navigate to the app's launch page; the location bar should be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/empty.html", false);

  // Navigate to the https version of the site; the location bar should
  // be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "https://www.example.com/blah", false);
}

// Check that the location bar is shown correctly for HTTPS bookmark apps when
// they navigate to a HTTP page on the same origin.
IN_PROC_BROWSER_TEST_F(HostedAppTest,
                       ShouldShowLocationBarForHTTPSBookmarkApp) {
  base::CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableNewBookmarkApps);

  SetupApp("https_app", true);

  // Navigate to the app's launch page; the location bar should be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "https://www.example.com/empty.html", false);

  // Navigate to the http version of the site; the location bar should
  // be visible for the https version as it is now on a less secure version
  // of its host.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/blah", true);
}

// Check that the location bar is shown correctly for normal hosted apps.
IN_PROC_BROWSER_TEST_F(HostedAppTest,
                       ShouldShowLocationBarForHostedApp) {
  SetupApp("app", false);

  // Navigate to the app's launch page; the location bar should be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/empty.html", false);

  // Navigate to another page on the same origin; the location bar should still
  // hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/blah", false);

  // Navigate to different origin; the location bar should now be visible.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.foo.com/blah", true);
}

// Check that the location bar is shown correctly for hosted apps that specify
// start URLs without the 'www.' prefix.
IN_PROC_BROWSER_TEST_F(HostedAppTest,
                       LocationBarForHostedAppWithoutWWW) {
  SetupApp("app_no_www", false);

  // Navigate to the app's launch page; the location bar should be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://example.com/empty.html", false);

  // Navigate to the app's launch page with the 'www.' prefis; the location bar
  // should be hidden.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.example.com/empty.html", false);

  // Navigate to different origin; the location bar should now be visible.
  NavigateAndCheckForLocationBar(
      app_browser_, "http://www.foo.com/blah", true);
}

// Open a normal browser window, a hosted app window, a legacy packaged app
// window and a dev tools window, and check that the web app frame feature is
// supported correctly.
IN_PROC_BROWSER_TEST_F(HostedAppTest, ShouldUseWebAppFrame) {
  base::CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableWebAppFrame);

  // Load a hosted app.
   const Extension* bookmark_app = InstallExtensionWithSourceAndFlags(
      test_data_dir_.AppendASCII("app"),
      1,
      extensions::Manifest::INTERNAL,
      extensions::Extension::FROM_BOOKMARK);
  ASSERT_TRUE(bookmark_app);

  // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
  WebContents* bookmark_app_window = OpenApplication(AppLaunchParams(
      browser()->profile(), bookmark_app, extensions::LAUNCH_CONTAINER_WINDOW,
      NEW_WINDOW, extensions::SOURCE_UNTRACKED));
  ASSERT_TRUE(bookmark_app_window);

  //  Load a packaged app.
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("packaged_app")));
  const Extension* packaged_app = nullptr;
  extensions::ExtensionRegistry* registry =
      extensions::ExtensionRegistry::Get(browser()->profile());
  for (const scoped_refptr<const extensions::Extension>& extension :
       registry->enabled_extensions()) {
    if (extension->name() == "Packaged App Test")
      packaged_app = extension.get();
  }
  ASSERT_TRUE(packaged_app);

  // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
  WebContents* packaged_app_window = OpenApplication(AppLaunchParams(
      browser()->profile(), packaged_app, extensions::LAUNCH_CONTAINER_WINDOW,
      NEW_WINDOW, extensions::SOURCE_UNTRACKED));
  ASSERT_TRUE(packaged_app_window);

  DevToolsWindow* devtools_window =
      DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), false);

  // The launch should have created a new app browser and a dev tools browser.
  ASSERT_EQ(4u, chrome::GetBrowserCount(browser()->profile(),
                                        browser()->host_desktop_type()));

  // Find the new browsers.
  Browser* bookmark_app_browser = nullptr;
  Browser* packaged_app_browser = nullptr;
  Browser* dev_tools_browser = nullptr;
  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
    if (*it == browser()) {
      continue;
    } else if ((*it)->app_name() == DevToolsWindow::kDevToolsApp) {
      dev_tools_browser = *it;
    } else if ((*it)->tab_strip_model()->GetActiveWebContents() ==
               bookmark_app_window) {
      bookmark_app_browser = *it;
    } else {
      packaged_app_browser = *it;
    }
  }
  ASSERT_TRUE(dev_tools_browser);
  ASSERT_TRUE(bookmark_app_browser);
  ASSERT_TRUE(bookmark_app_browser != browser());
  ASSERT_TRUE(packaged_app_browser);
  ASSERT_TRUE(packaged_app_browser != browser());
  ASSERT_TRUE(packaged_app_browser != bookmark_app_browser);

  EXPECT_FALSE(browser()->SupportsWindowFeature(Browser::FEATURE_WEBAPPFRAME));
  EXPECT_FALSE(
      dev_tools_browser->SupportsWindowFeature(Browser::FEATURE_WEBAPPFRAME));
  EXPECT_EQ(browser()->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH,
            bookmark_app_browser->SupportsWindowFeature(
                Browser::FEATURE_WEBAPPFRAME));
  EXPECT_FALSE(packaged_app_browser->SupportsWindowFeature(
      Browser::FEATURE_WEBAPPFRAME));

  DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
}
<|endoftext|>


<commit_before><commit_msg>Kill dead code.<commit_after><|endoftext|>


<commit_before>
// @(#)root/proof:$Id$
// Author: G. Ganis  Nov 2006

/*************************************************************************
 * Copyright (C) 1995-2005, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TProofChain                                                          //
//                                                                      //
// A TChain proxy on PROOF.                                             //
// Uses an internal TDSet to handle processing.                         //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TProofChain.h"
#include "TDSet.h"
#include "TList.h"
#include "TProof.h"
#include "TROOT.h"
#include "TEventList.h"
#include "TEntryList.h"

ClassImp(TProofChain)

//______________________________________________________________________________
TProofChain::TProofChain() : TChain()
{
   // Crates a new Proof chain proxy containing the files from the TDSet.

   fChain        = 0;
   fTree         = 0;
   fSet          = 0;
   fDirectory    = gDirectory;
   fDrawFeedback = 0;
}

//______________________________________________________________________________
TProofChain::TProofChain(TChain *chain) : TChain()
{
   // Crates a new Proof chain proxy containing the files from the TDSet.

   fChain        = chain;
   fTree         = 0;
   fSet          = chain ? new TDSet((const TChain &)(*chain)) : 0;
   fDirectory    = gDirectory;
   fDrawFeedback = 0;
   if (gProof)
      gProof->AddChain(chain);
}

//______________________________________________________________________________
TProofChain::~TProofChain()
{
   // Destructor

   if (fChain) {
      SafeDelete(fSet);
      // Remove the chain from the private lists in the TProof objects
      TIter nxp(gROOT->GetListOfSockets());
      TObject *o = 0;
      TProof *p = 0;
      while ((o = nxp()))
         if ((p = dynamic_cast<TProof *>(o)))
            p->RemoveChain(fChain);
      fChain->SetProof(0);
   } else {
      // Not owner
      fSet = 0;
   }
   SafeDelete(fTree);
   fDirectory    = 0;

}

//______________________________________________________________________________
TProofChain::TProofChain(TDSet *dset, Bool_t gettreeheader)
{
   // Constructor from existing data set

   fChain        = 0;
   fTree         = 0;
   fSet          = dset;
   fDirectory    = gDirectory;
   fDrawFeedback = 0;
   if (gProof) {
      ConnectProof();
      if (gettreeheader && dset)
         fTree = gProof->GetTreeHeader(dset);
   }
}

//______________________________________________________________________________
void TProofChain::Browse(TBrowser *b)
{
   // Forwards the execution to the dummy tree header.
   // See TTree::Browse().

   fSet->Browse(b);
}

//______________________________________________________________________________
Long64_t TProofChain::Draw(const char *varexp, const TCut &selection,
                           Option_t *option, Long64_t nentries, Long64_t firstentry)
{
   // Forwards the execution to the TDSet.
   // Returns -1 in case of error or number of selected events in case of success.
   // See TDSet::Browse().

   if (!gProof) {
      Error("Draw", "no active PROOF session");
      return -1;
   }
   ConnectProof();

   if (fDrawFeedback)
      gProof->SetDrawFeedbackOption(fDrawFeedback, option);
   fReadEntry = firstentry;

   // Set either the entry-list (priority) or the event-list
   if (fEntryList) {
      fSet->SetEntryList(fEntryList);
   } else if (fEventList) {
      fSet->SetEntryList(fEventList);
   }

   Long64_t rv = fSet->Draw(varexp, selection, option, nentries, firstentry);
   return rv;
}

//______________________________________________________________________________
Long64_t TProofChain::Draw(const char *varexp, const char *selection,
                           Option_t *option,Long64_t nentries, Long64_t firstentry)
{
   // Forwards the execution to the TDSet.
   // Returns -1 in case of error or number of selected events in case of success.
   // See TDSet::Browse().

   if (!gProof) {
      Error("Draw", "no active PROOF session");
      return -1;
   }
   ConnectProof();

   if (fDrawFeedback)
      gProof->SetDrawFeedbackOption(fDrawFeedback, option);
   fReadEntry = firstentry;

   // Set either the entry-list (priority) or the event-list
   if (fEntryList) {
      fSet->SetEntryList(fEntryList);
   } else if (fEventList) {
      fSet->SetEntryList(fEventList);
   }

   Long64_t rv = fSet->Draw(varexp, selection, option, nentries, firstentry);
   return rv;
}

//______________________________________________________________________________
TBranch *TProofChain::FindBranch(const char* branchname)
{
   // Forwards the execution to the dummy tree header.
   // See TTree::FindBranch().

   return (fTree ? fTree->FindBranch(branchname) : (TBranch *)0);
}

//______________________________________________________________________________
TLeaf *TProofChain::FindLeaf(const char* searchname)
{
   // Forwards the execution to the dummy tree header.
   // See TTree::FindLeaf().

   return (fTree ? fTree->FindLeaf(searchname) : (TLeaf *)0);
}

//______________________________________________________________________________
TBranch *TProofChain::GetBranch(const char *name)
{
   // Forwards the execution to the dummy tree header.
   // See TTree::GetBranch().

   return (fTree ? fTree->GetBranch(name) : (TBranch *)0);
}

//______________________________________________________________________________
Bool_t TProofChain::GetBranchStatus(const char *branchname) const
{
   // Forwards the execution to the dummy tree header.
   // See TTree::GetBranchStatus().

   return (fTree ? fTree->GetBranchStatus(branchname) : kFALSE);
}

//______________________________________________________________________________
TVirtualTreePlayer *TProofChain::GetPlayer()
{
   // Forwards the execution to the dummy tree header.
   // See TTree::GetPlayer().

   if (!fTree)
      if (gProof) {
         fTree = gProof->GetTreeHeader(fSet);
         ConnectProof();
      }

   return (fTree ? fTree->GetPlayer() : (TVirtualTreePlayer *)0);
}

//______________________________________________________________________________
Long64_t TProofChain::Process(const char *filename, Option_t *option,
                              Long64_t nentries, Long64_t firstentry)
{
   // Forwards the execution to the TDSet.
   // The return value is -1 in case of error and TSelector::GetStatus() in
   // in case of success.
   // See TDSet::Process().

   // Set either the entry-list (priority) or the event-list
   if (fEntryList) {
      fSet->SetEntryList(fEntryList);
   } else if (fEventList) {
      fSet->SetEntryList(fEventList);
   }

   return fSet->Process(filename, option, nentries, firstentry);
}

//______________________________________________________________________________
Long64_t TProofChain::Process(TSelector *selector, Option_t *option,
                              Long64_t nentries, Long64_t firstentry)
{
   // Not implemented in TProofChain. Shouldn't be used.
   // The return value is -1 in case of error and TSelector::GetStatus() in
   // in case of success.

   if (selector || option || nentries || firstentry) { }
   //   return fSet->Process(selector, option, nentries, firstentry);
   Warning("Process", "not implemented"); // TODO
   return -1;
}

//______________________________________________________________________________
void TProofChain::SetDebug(Int_t level, Long64_t min, Long64_t max)
{
   // See TTree::SetDebug

   TTree::SetDebug(level, min, max);
}

//______________________________________________________________________________
void TProofChain::SetName(const char *name)
{
   // See TTree::GetName.

   TTree::SetName(name);
}

//______________________________________________________________________________
Long64_t TProofChain::GetEntries() const
{
   // Returns the total number of entries in the TProofChain, which is
   // the number of entries in the TDSet that it holds.

   // this was used for holding the total number of entries
   return (fTree ? fTree->GetMaxEntryLoop() : (Long64_t)(-1));
}

//______________________________________________________________________________
Long64_t TProofChain::GetEntries(const char *)
{
   // See TTree::GetEntries(const char *selection)
   // Not implemented in TProofChain. Shouldn't be used.

   return Long64_t(-1);
}

//______________________________________________________________________________
void TProofChain::Progress(Long64_t total, Long64_t processed)
{
   // Changes the number of processed entries.

   if (gROOT->IsInterrupted() && gProof)
      gProof->StopProcess(kTRUE);
   if (total) { }

   fReadEntry = processed;
}

//______________________________________________________________________________
Long64_t TProofChain::GetReadEntry() const
{
   // Returns the number of processed entries.

   return fReadEntry;
}

//______________________________________________________________________________
void TProofChain::ReleaseProof()
{
   // Releases PROOF. Disconnect the "progress" signal.

   if (!gProof)
      return;
   gProof->Disconnect("Progress(Long64_t,Long64_t)",
                      this, "Progress(Long64_t,Long64_t)");
   if (fDrawFeedback)
      gProof->DeleteDrawFeedback(fDrawFeedback);
   fDrawFeedback = 0;
}

//______________________________________________________________________________
void TProofChain::ConnectProof()
{
   // Connects the proof - creates a "DrawFeedback" and connects the
   // "Progress" signal.

   if (gProof && !fDrawFeedback) {
      fDrawFeedback = gProof->CreateDrawFeedback();

      gProof->Connect("Progress(Long64_t,Long64_t)", "TProofChain",
                       this, "Progress(Long64_t,Long64_t)");
   }
}
<commit_msg>Avoid recursive destruction (fix bug #30608)<commit_after>
// @(#)root/proof:$Id$
// Author: G. Ganis  Nov 2006

/*************************************************************************
 * Copyright (C) 1995-2005, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TProofChain                                                          //
//                                                                      //
// A TChain proxy on PROOF.                                             //
// Uses an internal TDSet to handle processing.                         //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TProofChain.h"
#include "TDSet.h"
#include "TList.h"
#include "TProof.h"
#include "TROOT.h"
#include "TEventList.h"
#include "TEntryList.h"

ClassImp(TProofChain)

//______________________________________________________________________________
TProofChain::TProofChain() : TChain()
{
   // Crates a new Proof chain proxy containing the files from the TDSet.

   fChain        = 0;
   fTree         = 0;
   fSet          = 0;
   fDirectory    = gDirectory;
   fDrawFeedback = 0;
}

//______________________________________________________________________________
TProofChain::TProofChain(TChain *chain) : TChain()
{
   // Crates a new Proof chain proxy containing the files from the TDSet.

   fChain        = chain;
   fTree         = 0;
   fSet          = chain ? new TDSet((const TChain &)(*chain)) : 0;
   fDirectory    = gDirectory;
   fDrawFeedback = 0;
   if (gProof)
      gProof->AddChain(chain);
}

//______________________________________________________________________________
TProofChain::~TProofChain()
{
   // Destructor

   if (fChain) {
      SafeDelete(fSet);
      // Remove the chain from the private lists in the TProof objects
      TIter nxp(gROOT->GetListOfSockets());
      TObject *o = 0;
      TProof *p = 0;
      while ((o = nxp()))
         if ((p = dynamic_cast<TProof *>(o)))
            p->RemoveChain(fChain);
      fChain = 0;
   } else {
      // Not owner
      fSet = 0;
   }
   SafeDelete(fTree);
   fDirectory    = 0;

}

//______________________________________________________________________________
TProofChain::TProofChain(TDSet *dset, Bool_t gettreeheader)
{
   // Constructor from existing data set

   fChain        = 0;
   fTree         = 0;
   fSet          = dset;
   fDirectory    = gDirectory;
   fDrawFeedback = 0;
   if (gProof) {
      ConnectProof();
      if (gettreeheader && dset)
         fTree = gProof->GetTreeHeader(dset);
   }
}

//______________________________________________________________________________
void TProofChain::Browse(TBrowser *b)
{
   // Forwards the execution to the dummy tree header.
   // See TTree::Browse().

   fSet->Browse(b);
}

//______________________________________________________________________________
Long64_t TProofChain::Draw(const char *varexp, const TCut &selection,
                           Option_t *option, Long64_t nentries, Long64_t firstentry)
{
   // Forwards the execution to the TDSet.
   // Returns -1 in case of error or number of selected events in case of success.
   // See TDSet::Browse().

   if (!gProof) {
      Error("Draw", "no active PROOF session");
      return -1;
   }
   ConnectProof();

   if (fDrawFeedback)
      gProof->SetDrawFeedbackOption(fDrawFeedback, option);
   fReadEntry = firstentry;

   // Set either the entry-list (priority) or the event-list
   if (fEntryList) {
      fSet->SetEntryList(fEntryList);
   } else if (fEventList) {
      fSet->SetEntryList(fEventList);
   }

   Long64_t rv = fSet->Draw(varexp, selection, option, nentries, firstentry);
   return rv;
}

//______________________________________________________________________________
Long64_t TProofChain::Draw(const char *varexp, const char *selection,
                           Option_t *option,Long64_t nentries, Long64_t firstentry)
{
   // Forwards the execution to the TDSet.
   // Returns -1 in case of error or number of selected events in case of success.
   // See TDSet::Browse().

   if (!gProof) {
      Error("Draw", "no active PROOF session");
      return -1;
   }
   ConnectProof();

   if (fDrawFeedback)
      gProof->SetDrawFeedbackOption(fDrawFeedback, option);
   fReadEntry = firstentry;

   // Set either the entry-list (priority) or the event-list
   if (fEntryList) {
      fSet->SetEntryList(fEntryList);
   } else if (fEventList) {
      fSet->SetEntryList(fEventList);
   }

   Long64_t rv = fSet->Draw(varexp, selection, option, nentries, firstentry);
   return rv;
}

//______________________________________________________________________________
TBranch *TProofChain::FindBranch(const char* branchname)
{
   // Forwards the execution to the dummy tree header.
   // See TTree::FindBranch().

   return (fTree ? fTree->FindBranch(branchname) : (TBranch *)0);
}

//______________________________________________________________________________
TLeaf *TProofChain::FindLeaf(const char* searchname)
{
   // Forwards the execution to the dummy tree header.
   // See TTree::FindLeaf().

   return (fTree ? fTree->FindLeaf(searchname) : (TLeaf *)0);
}

//______________________________________________________________________________
TBranch *TProofChain::GetBranch(const char *name)
{
   // Forwards the execution to the dummy tree header.
   // See TTree::GetBranch().

   return (fTree ? fTree->GetBranch(name) : (TBranch *)0);
}

//______________________________________________________________________________
Bool_t TProofChain::GetBranchStatus(const char *branchname) const
{
   // Forwards the execution to the dummy tree header.
   // See TTree::GetBranchStatus().

   return (fTree ? fTree->GetBranchStatus(branchname) : kFALSE);
}

//______________________________________________________________________________
TVirtualTreePlayer *TProofChain::GetPlayer()
{
   // Forwards the execution to the dummy tree header.
   // See TTree::GetPlayer().

   if (!fTree)
      if (gProof) {
         fTree = gProof->GetTreeHeader(fSet);
         ConnectProof();
      }

   return (fTree ? fTree->GetPlayer() : (TVirtualTreePlayer *)0);
}

//______________________________________________________________________________
Long64_t TProofChain::Process(const char *filename, Option_t *option,
                              Long64_t nentries, Long64_t firstentry)
{
   // Forwards the execution to the TDSet.
   // The return value is -1 in case of error and TSelector::GetStatus() in
   // in case of success.
   // See TDSet::Process().

   // Set either the entry-list (priority) or the event-list
   if (fEntryList) {
      fSet->SetEntryList(fEntryList);
   } else if (fEventList) {
      fSet->SetEntryList(fEventList);
   }

   return fSet->Process(filename, option, nentries, firstentry);
}

//______________________________________________________________________________
Long64_t TProofChain::Process(TSelector *selector, Option_t *option,
                              Long64_t nentries, Long64_t firstentry)
{
   // Not implemented in TProofChain. Shouldn't be used.
   // The return value is -1 in case of error and TSelector::GetStatus() in
   // in case of success.

   if (selector || option || nentries || firstentry) { }
   //   return fSet->Process(selector, option, nentries, firstentry);
   Warning("Process", "not implemented"); // TODO
   return -1;
}

//______________________________________________________________________________
void TProofChain::SetDebug(Int_t level, Long64_t min, Long64_t max)
{
   // See TTree::SetDebug

   TTree::SetDebug(level, min, max);
}

//______________________________________________________________________________
void TProofChain::SetName(const char *name)
{
   // See TTree::GetName.

   TTree::SetName(name);
}

//______________________________________________________________________________
Long64_t TProofChain::GetEntries() const
{
   // Returns the total number of entries in the TProofChain, which is
   // the number of entries in the TDSet that it holds.

   // this was used for holding the total number of entries
   return (fTree ? fTree->GetMaxEntryLoop() : (Long64_t)(-1));
}

//______________________________________________________________________________
Long64_t TProofChain::GetEntries(const char *)
{
   // See TTree::GetEntries(const char *selection)
   // Not implemented in TProofChain. Shouldn't be used.

   return Long64_t(-1);
}

//______________________________________________________________________________
void TProofChain::Progress(Long64_t total, Long64_t processed)
{
   // Changes the number of processed entries.

   if (gROOT->IsInterrupted() && gProof)
      gProof->StopProcess(kTRUE);
   if (total) { }

   fReadEntry = processed;
}

//______________________________________________________________________________
Long64_t TProofChain::GetReadEntry() const
{
   // Returns the number of processed entries.

   return fReadEntry;
}

//______________________________________________________________________________
void TProofChain::ReleaseProof()
{
   // Releases PROOF. Disconnect the "progress" signal.

   if (!gProof)
      return;
   gProof->Disconnect("Progress(Long64_t,Long64_t)",
                      this, "Progress(Long64_t,Long64_t)");
   if (fDrawFeedback)
      gProof->DeleteDrawFeedback(fDrawFeedback);
   fDrawFeedback = 0;
}

//______________________________________________________________________________
void TProofChain::ConnectProof()
{
   // Connects the proof - creates a "DrawFeedback" and connects the
   // "Progress" signal.

   if (gProof && !fDrawFeedback) {
      fDrawFeedback = gProof->CreateDrawFeedback();

      gProof->Connect("Progress(Long64_t,Long64_t)", "TProofChain",
                       this, "Progress(Long64_t,Long64_t)");
   }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "module-loader.h"
#include "lexer.h"
#include "parser.h"
#include <kj/vector.h>
#include <kj/mutex.h>
#include <kj/debug.h>
#include <kj/io.h>
#include <capnp/message.h>
#include <map>

namespace capnp {
namespace compiler {

class ModuleLoader::Impl {
public:
  Impl(GlobalErrorReporter& errorReporter)
      : errorReporter(errorReporter) {}

  void addImportPath(kj::ReadableDirectory& dir) {
    searchPath.add(&dir);
  }

  kj::Maybe<Module&> loadModule(kj::ReadableDirectory& dir, kj::PathPtr path);
  kj::Maybe<Module&> loadModuleFromSearchPath(kj::PathPtr path);
  kj::Maybe<kj::Array<const byte>> readEmbed(kj::ReadableDirectory& dir, kj::PathPtr path);
  kj::Maybe<kj::Array<const byte>> readEmbedFromSearchPath(kj::PathPtr path);
  GlobalErrorReporter& getErrorReporter() { return errorReporter; }

private:
  GlobalErrorReporter& errorReporter;
  kj::Vector<kj::ReadableDirectory*> searchPath;
  std::map<std::pair<kj::ReadableDirectory*, kj::PathPtr>, kj::Own<Module>> modules;
};

class ModuleLoader::ModuleImpl final: public Module {
public:
  ModuleImpl(ModuleLoader::Impl& loader, kj::Own<kj::ReadableFile> file,
             kj::ReadableDirectory& sourceDir, kj::PathPtr path)
      : loader(loader), file(kj::mv(file)), sourceDir(sourceDir), path(path.clone()),
        sourceNameStr(path.toString()) {
    KJ_REQUIRE(path.size() > 0);
  }

  kj::PathPtr getPath() {
    return path;
  }

  kj::StringPtr getSourceName() override {
    return sourceNameStr;
  }

  Orphan<ParsedFile> loadContent(Orphanage orphanage) override {
    kj::Array<const char> content = file->mmap(0, file->stat().size).releaseAsChars();

    lineBreaks = nullptr;  // In case loadContent() is called multiple times.
    lineBreaks = lineBreaksSpace.construct(content);

    MallocMessageBuilder lexedBuilder;
    auto statements = lexedBuilder.initRoot<LexedStatements>();
    lex(content, statements, *this);

    auto parsed = orphanage.newOrphan<ParsedFile>();
    parseFile(statements.getStatements(), parsed.get(), *this);
    return parsed;
  }

  kj::Maybe<Module&> importRelative(kj::StringPtr importPath) override {
    if (importPath.size() > 0 && importPath[0] == '/') {
      return loader.loadModuleFromSearchPath(kj::Path::parse(importPath.slice(1)));
    } else {
      return loader.loadModule(sourceDir, path.parent().eval(importPath));
    }
  }

  kj::Maybe<kj::Array<const byte>> embedRelative(kj::StringPtr embedPath) override {
    if (embedPath.size() > 0 && embedPath[0] == '/') {
      return loader.readEmbedFromSearchPath(kj::Path::parse(embedPath.slice(1)));
    } else {
      return loader.readEmbed(sourceDir, path.parent().eval(embedPath));
    }
  }

  void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) override {
    auto& lines = *KJ_REQUIRE_NONNULL(lineBreaks,
        "Can't report errors until loadContent() is called.");

    loader.getErrorReporter().addError(sourceDir, path,
        lines.toSourcePos(startByte), lines.toSourcePos(endByte), message);
  }

  bool hadErrors() override {
    return loader.getErrorReporter().hadErrors();
  }

private:
  ModuleLoader::Impl& loader;
  kj::Own<kj::ReadableFile> file;
  kj::ReadableDirectory& sourceDir;
  kj::Path path;
  kj::String sourceNameStr;

  kj::SpaceFor<LineBreakTable> lineBreaksSpace;
  kj::Maybe<kj::Own<LineBreakTable>> lineBreaks;
};

// =======================================================================================

kj::Maybe<Module&> ModuleLoader::Impl::loadModule(
    kj::ReadableDirectory& dir, kj::PathPtr path) {
  auto iter = modules.find(std::make_pair(&dir, path));
  if (iter != modules.end()) {
    // Return existing file.
    return *iter->second;
  }

  KJ_IF_MAYBE(file, dir.tryOpenFile(path)) {
    auto module = kj::heap<ModuleImpl>(*this, kj::mv(*file), dir, path);
    auto& result = *module;
    modules.insert(std::make_pair(std::make_pair(&dir, result.getPath()), kj::mv(module)));
    return result;
  } else {
    // No such file.
    return nullptr;
  }
}

kj::Maybe<Module&> ModuleLoader::Impl::loadModuleFromSearchPath(kj::PathPtr path) {
  for (auto candidate: searchPath) {
    KJ_IF_MAYBE(module, loadModule(*candidate, path)) {
      return *module;
    }
  }
  return nullptr;
}

kj::Maybe<kj::Array<const byte>> ModuleLoader::Impl::readEmbed(
    kj::ReadableDirectory& dir, kj::PathPtr path) {
  KJ_IF_MAYBE(file, dir.tryOpenFile(path)) {
    return file->get()->mmap(0, file->get()->stat().size);
  }
  return nullptr;
}

kj::Maybe<kj::Array<const byte>> ModuleLoader::Impl::readEmbedFromSearchPath(kj::PathPtr path) {
  for (auto candidate: searchPath) {
    KJ_IF_MAYBE(module, readEmbed(*candidate, path)) {
      return kj::mv(*module);
    }
  }
  return nullptr;
}

// =======================================================================================

ModuleLoader::ModuleLoader(GlobalErrorReporter& errorReporter)
    : impl(kj::heap<Impl>(errorReporter)) {}
ModuleLoader::~ModuleLoader() noexcept(false) {}

void ModuleLoader::addImportPath(kj::ReadableDirectory& dir) {
  impl->addImportPath(dir);
}

kj::Maybe<Module&> ModuleLoader::loadModule(kj::ReadableDirectory& dir, kj::PathPtr path) {
  return impl->loadModule(dir, path);
}

}  // namespace compiler
}  // namespace capnp
<commit_msg>Fix cmake: Use heuristics to detect when the same file is mapped in multiple locations.<commit_after>// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "module-loader.h"
#include "lexer.h"
#include "parser.h"
#include <kj/vector.h>
#include <kj/mutex.h>
#include <kj/debug.h>
#include <kj/io.h>
#include <capnp/message.h>
#include <unordered_map>

namespace capnp {
namespace compiler {

namespace {

struct FileKey {
  // Key type for the modules map. We need to implement some complicated heuristics to detect when
  // two files are actually the same underlying file on disk, in order to handle the case where
  // people have mapped the same file into multiple locations in the import tree, whether by
  // passing overlapping import paths, weird symlinks, or whatever.
  //
  // This is probably over-engineered.

  kj::ReadableDirectory& baseDir;
  kj::PathPtr path;
  kj::ReadableFile* file;  // should be Maybe<ReadableFile&> but annoying const-copy issues come up.
  uint64_t hashCode;
  uint64_t size;
  kj::Date lastModified;

  FileKey(kj::ReadableDirectory& baseDir, kj::PathPtr path)
      : baseDir(baseDir), path(path), file(nullptr),
        hashCode(0), size(0), lastModified(kj::UNIX_EPOCH) {}
  FileKey(kj::ReadableDirectory& baseDir, kj::PathPtr path, kj::ReadableFile& file)
      : FileKey(baseDir, path, file, file.stat()) {}

  FileKey(kj::ReadableDirectory& baseDir, kj::PathPtr path, kj::ReadableFile& file,
          kj::FsNode::Metadata meta)
      : baseDir(baseDir), path(path), file(&file),
        hashCode(meta.hashCode), size(meta.size), lastModified(meta.lastModified) {}

  bool operator==(const FileKey& other) const {
    // Allow matching on baseDir and path without a file.
    if (&baseDir == &other.baseDir && path == other.path) return true;
    if (file == nullptr || other.file == nullptr) return false;

    // Try comparing various file metadata to rule out obvious differences.
    if (hashCode != other.hashCode) return false;
    if (size != other.size || lastModified != other.lastModified) return false;
    if (path.size() > 0 && other.path.size() > 0 &&
        path[path.size() - 1] != other.path[other.path.size() - 1]) {
      // Names differ, so probably not the same file.
      return false;
    }

    // Same file hash, but different paths, but same size and modification date. This could be a
    // case of two different import paths overlapping and containing the same file. We'll need to
    // check the content.
    auto mapping1 = file->mmap(0, size);
    auto mapping2 = other.file->mmap(0, size);
    if (memcmp(mapping1.begin(), mapping2.begin(), size) != 0) return false;

    if (path == other.path) {
      // Exactly the same content was mapped at exactly the same path relative to two different
      // import directories. This can only really happen if this was one of the files passed on
      // the command line, but its --src-prefix is not also an import path, but some other
      // directory containing the same file was given as an import path. Whatever, we'll ignore
      // this.
      return true;
    }

    // Exactly the same content!
    static bool warned = false;
    if (!warned) {
      KJ_LOG(WARNING,
          "Found exactly the same source file mapped at two different paths. This suggests "
          "that your -I and --src-prefix flags are overlapping or inconsistent. Remember, these "
          "flags should only specify directories that are logical 'roots' of the source tree. "
          "It should never be the case that one of the import directories contains another one of "
          "them.",
          path, other.path);
      warned = true;
    }

    return true;
  }
};

struct FileKeyHash {
  size_t operator()(const FileKey& key) const {
    if (sizeof(size_t) < sizeof(key.hashCode)) {
      // 32-bit system, do more mixing
      return (key.hashCode >> 32) * 31 + static_cast<size_t>(key.hashCode) +
          key.size * 103 + (key.lastModified - kj::UNIX_EPOCH) / kj::MILLISECONDS * 73;
    } else {
      return key.hashCode + key.size * 103 +
          (key.lastModified - kj::UNIX_EPOCH) / kj::NANOSECONDS * 73;
    }
  }
};

};

class ModuleLoader::Impl {
public:
  Impl(GlobalErrorReporter& errorReporter)
      : errorReporter(errorReporter) {}

  void addImportPath(kj::ReadableDirectory& dir) {
    searchPath.add(&dir);
  }

  kj::Maybe<Module&> loadModule(kj::ReadableDirectory& dir, kj::PathPtr path);
  kj::Maybe<Module&> loadModuleFromSearchPath(kj::PathPtr path);
  kj::Maybe<kj::Array<const byte>> readEmbed(kj::ReadableDirectory& dir, kj::PathPtr path);
  kj::Maybe<kj::Array<const byte>> readEmbedFromSearchPath(kj::PathPtr path);
  GlobalErrorReporter& getErrorReporter() { return errorReporter; }

private:
  GlobalErrorReporter& errorReporter;
  kj::Vector<kj::ReadableDirectory*> searchPath;
  std::unordered_map<FileKey, kj::Own<Module>, FileKeyHash> modules;
};

class ModuleLoader::ModuleImpl final: public Module {
public:
  ModuleImpl(ModuleLoader::Impl& loader, kj::Own<kj::ReadableFile> file,
             kj::ReadableDirectory& sourceDir, kj::Path pathParam)
      : loader(loader), file(kj::mv(file)), sourceDir(sourceDir), path(kj::mv(pathParam)),
        sourceNameStr(path.toString()) {
    KJ_REQUIRE(path.size() > 0);
  }

  kj::PathPtr getPath() {
    return path;
  }

  kj::StringPtr getSourceName() override {
    return sourceNameStr;
  }

  Orphan<ParsedFile> loadContent(Orphanage orphanage) override {
    kj::Array<const char> content = file->mmap(0, file->stat().size).releaseAsChars();

    lineBreaks = nullptr;  // In case loadContent() is called multiple times.
    lineBreaks = lineBreaksSpace.construct(content);

    MallocMessageBuilder lexedBuilder;
    auto statements = lexedBuilder.initRoot<LexedStatements>();
    lex(content, statements, *this);

    auto parsed = orphanage.newOrphan<ParsedFile>();
    parseFile(statements.getStatements(), parsed.get(), *this);
    return parsed;
  }

  kj::Maybe<Module&> importRelative(kj::StringPtr importPath) override {
    if (importPath.size() > 0 && importPath[0] == '/') {
      return loader.loadModuleFromSearchPath(kj::Path::parse(importPath.slice(1)));
    } else {
      return loader.loadModule(sourceDir, path.parent().eval(importPath));
    }
  }

  kj::Maybe<kj::Array<const byte>> embedRelative(kj::StringPtr embedPath) override {
    if (embedPath.size() > 0 && embedPath[0] == '/') {
      return loader.readEmbedFromSearchPath(kj::Path::parse(embedPath.slice(1)));
    } else {
      return loader.readEmbed(sourceDir, path.parent().eval(embedPath));
    }
  }

  void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) override {
    auto& lines = *KJ_REQUIRE_NONNULL(lineBreaks,
        "Can't report errors until loadContent() is called.");

    loader.getErrorReporter().addError(sourceDir, path,
        lines.toSourcePos(startByte), lines.toSourcePos(endByte), message);
  }

  bool hadErrors() override {
    return loader.getErrorReporter().hadErrors();
  }

private:
  ModuleLoader::Impl& loader;
  kj::Own<kj::ReadableFile> file;
  kj::ReadableDirectory& sourceDir;
  kj::Path path;
  kj::String sourceNameStr;

  kj::SpaceFor<LineBreakTable> lineBreaksSpace;
  kj::Maybe<kj::Own<LineBreakTable>> lineBreaks;
};

// =======================================================================================

kj::Maybe<Module&> ModuleLoader::Impl::loadModule(
    kj::ReadableDirectory& dir, kj::PathPtr path) {
  auto iter = modules.find(FileKey(dir, path));
  if (iter != modules.end()) {
    // Return existing file.
    return *iter->second;
  }

  KJ_IF_MAYBE(file, dir.tryOpenFile(path)) {
    auto pathCopy = path.clone();
    auto key = FileKey(dir, pathCopy, **file);
    auto module = kj::heap<ModuleImpl>(*this, kj::mv(*file), dir, kj::mv(pathCopy));
    auto& result = *module;
    auto insertResult = modules.insert(std::make_pair(key, kj::mv(module)));
    if (insertResult.second) {
      return result;
    } else {
      // Now that we have the file open, we noticed a collision. Return the old file.
      return *insertResult.first->second;
    }
  } else {
    // No such file.
    return nullptr;
  }
}

kj::Maybe<Module&> ModuleLoader::Impl::loadModuleFromSearchPath(kj::PathPtr path) {
  for (auto candidate: searchPath) {
    KJ_IF_MAYBE(module, loadModule(*candidate, path)) {
      return *module;
    }
  }
  return nullptr;
}

kj::Maybe<kj::Array<const byte>> ModuleLoader::Impl::readEmbed(
    kj::ReadableDirectory& dir, kj::PathPtr path) {
  KJ_IF_MAYBE(file, dir.tryOpenFile(path)) {
    return file->get()->mmap(0, file->get()->stat().size);
  }
  return nullptr;
}

kj::Maybe<kj::Array<const byte>> ModuleLoader::Impl::readEmbedFromSearchPath(kj::PathPtr path) {
  for (auto candidate: searchPath) {
    KJ_IF_MAYBE(module, readEmbed(*candidate, path)) {
      return kj::mv(*module);
    }
  }
  return nullptr;
}

// =======================================================================================

ModuleLoader::ModuleLoader(GlobalErrorReporter& errorReporter)
    : impl(kj::heap<Impl>(errorReporter)) {}
ModuleLoader::~ModuleLoader() noexcept(false) {}

void ModuleLoader::addImportPath(kj::ReadableDirectory& dir) {
  impl->addImportPath(dir);
}

kj::Maybe<Module&> ModuleLoader::loadModule(kj::ReadableDirectory& dir, kj::PathPtr path) {
  return impl->loadModule(dir, path);
}

}  // namespace compiler
}  // namespace capnp
<|endoftext|>


<commit_before><commit_msg>coverity#737139 Uncaught exception<commit_after><|endoftext|>


<commit_before>#include <Nazara/Utility.hpp>
#include <Nazara/Renderer.hpp>
#include <Nazara/Shader.hpp>
#include <Nazara/Shader/SpirvConstantCache.hpp>
#include <Nazara/Shader/SpirvPrinter.hpp>
#include <array>
#include <iostream>

int main()
{
	Nz::Initializer<Nz::Renderer> loader;
	if (!loader)
	{
		std::cout << "Failed to initialize Vulkan" << std::endl;
		return __LINE__;
	}

	Nz::RenderWindow window;

	Nz::MeshParams meshParams;
	meshParams.matrix = Nz::Matrix4f::Rotate(Nz::EulerAnglesf(0.f, 90.f, 180.f)) * Nz::Matrix4f::Scale(Nz::Vector3f(0.002f));
	meshParams.vertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout_XYZ_Normal_UV);

	Nz::String windowTitle = "Vulkan Test";
	if (!window.Create(Nz::VideoMode(800, 600, 32), windowTitle))
	{
		std::cout << "Failed to create Window" << std::endl;
		return __LINE__;
	}

	std::shared_ptr<Nz::RenderDevice> device = window.GetRenderDevice();

	auto fragmentShader = device->InstantiateShaderStage(Nz::ShaderStageType::Fragment, Nz::ShaderLanguage::NazaraBinary, "frag.shader");
	if (!fragmentShader)
	{
		std::cout << "Failed to instantiate fragment shader" << std::endl;
		return __LINE__;
	}

	auto vertexShader = device->InstantiateShaderStage(Nz::ShaderStageType::Vertex, Nz::ShaderLanguage::NazaraBinary, "vert.shader");
	if (!vertexShader)
	{
		std::cout << "Failed to instantiate fragment shader" << std::endl;
		return __LINE__;
	}

	Nz::MeshRef drfreak = Nz::Mesh::LoadFromFile("resources/Spaceship/spaceship.obj", meshParams);

	if (!drfreak)
	{
		NazaraError("Failed to load model");
		return __LINE__;
	}

	Nz::StaticMesh* drfreakMesh = static_cast<Nz::StaticMesh*>(drfreak->GetSubMesh(0));

	const Nz::VertexBuffer* drfreakVB = drfreakMesh->GetVertexBuffer();
	const Nz::IndexBuffer* drfreakIB = drfreakMesh->GetIndexBuffer();

	// Index buffer
	std::cout << "Index count: " << drfreakIB->GetIndexCount() << std::endl;

	// Vertex buffer
	std::cout << "Vertex count: " << drfreakVB->GetVertexCount() << std::endl;

	// Texture
	Nz::ImageRef drfreakImage = Nz::Image::LoadFromFile("resources/Spaceship/Texture/diffuse.png");
	if (!drfreakImage || !drfreakImage->Convert(Nz::PixelFormat_RGBA8))
	{
		NazaraError("Failed to load image");
		return __LINE__;
	}

	Nz::TextureInfo texParams;
	texParams.pixelFormat = drfreakImage->GetFormat();
	texParams.type = drfreakImage->GetType();
	texParams.width = drfreakImage->GetWidth();
	texParams.height = drfreakImage->GetHeight();
	texParams.depth = drfreakImage->GetDepth();

	std::unique_ptr<Nz::Texture> texture = device->InstantiateTexture(texParams);
	if (!texture->Update(drfreakImage->GetConstPixels()))
	{
		NazaraError("Failed to update texture");
		return __LINE__;
	}

	std::unique_ptr<Nz::TextureSampler> textureSampler = device->InstantiateTextureSampler({});

	struct
	{
		Nz::Matrix4f projectionMatrix;
		Nz::Matrix4f modelMatrix;
		Nz::Matrix4f viewMatrix;
	}
	ubo;

	Nz::Vector2ui windowSize = window.GetSize();
	ubo.projectionMatrix = Nz::Matrix4f::Perspective(70.f, float(windowSize.x) / windowSize.y, 0.1f, 1000.f);
	ubo.viewMatrix = Nz::Matrix4f::Translate(Nz::Vector3f::Backward() * 1);
	ubo.modelMatrix = Nz::Matrix4f::Translate(Nz::Vector3f::Forward() * 2 + Nz::Vector3f::Right());

	Nz::UInt32 uniformSize = sizeof(ubo);

	Nz::RenderPipelineLayoutInfo pipelineLayoutInfo;
	auto& uboBinding = pipelineLayoutInfo.bindings.emplace_back();
	uboBinding.index = 0;
	uboBinding.shaderStageFlags = Nz::ShaderStageType::Vertex;
	uboBinding.type = Nz::ShaderBindingType::UniformBuffer;

	auto& textureBinding = pipelineLayoutInfo.bindings.emplace_back();
	textureBinding.index = 1;
	textureBinding.shaderStageFlags = Nz::ShaderStageType::Fragment;
	textureBinding.type = Nz::ShaderBindingType::Texture;

	std::shared_ptr<Nz::RenderPipelineLayout> renderPipelineLayout = device->InstantiateRenderPipelineLayout(std::move(pipelineLayoutInfo));

	Nz::ShaderBindingPtr shaderBinding = renderPipelineLayout->AllocateShaderBinding();

	std::unique_ptr<Nz::AbstractBuffer> uniformBuffer = device->InstantiateBuffer(Nz::BufferType_Uniform);
	if (!uniformBuffer->Initialize(uniformSize, Nz::BufferUsage_DeviceLocal))
	{
		NazaraError("Failed to create uniform buffer");
		return __LINE__;
	}

	shaderBinding->Update({
		{
			0,
			Nz::ShaderBinding::UniformBufferBinding {
				uniformBuffer.get(), 0, uniformSize
			}
		},
		{
			1,
			Nz::ShaderBinding::TextureBinding {
				texture.get(), textureSampler.get()
			}
		}
	});

	Nz::RenderPipelineInfo pipelineInfo;
	pipelineInfo.pipelineLayout = renderPipelineLayout;

	pipelineInfo.depthBuffer = true;
	pipelineInfo.shaderStages.emplace_back(fragmentShader);
	pipelineInfo.shaderStages.emplace_back(vertexShader);

	auto& vertexBuffer = pipelineInfo.vertexBuffers.emplace_back();
	vertexBuffer.binding = 0;
	vertexBuffer.declaration = drfreakVB->GetVertexDeclaration();

	std::unique_ptr<Nz::RenderPipeline> pipeline = device->InstantiateRenderPipeline(pipelineInfo);

	Nz::RenderDevice* renderDevice = window.GetRenderDevice().get();

	Nz::RenderWindowImpl* windowImpl = window.GetImpl();
	std::unique_ptr<Nz::CommandPool> commandPool = windowImpl->CreateCommandPool(Nz::QueueType::Graphics);

	Nz::RenderBuffer* renderBufferIB = static_cast<Nz::RenderBuffer*>(drfreakIB->GetBuffer()->GetImpl());
	Nz::RenderBuffer* renderBufferVB = static_cast<Nz::RenderBuffer*>(drfreakVB->GetBuffer()->GetImpl());

	if (!renderBufferIB->Synchronize(renderDevice))
	{
		NazaraError("Failed to synchronize render buffer");
		return __LINE__;
	}

	if (!renderBufferVB->Synchronize(renderDevice))
	{
		NazaraError("Failed to synchronize render buffer");
		return __LINE__;
	}

	Nz::AbstractBuffer* indexBufferImpl = renderBufferIB->GetHardwareBuffer(renderDevice);
	Nz::AbstractBuffer* vertexBufferImpl = renderBufferVB->GetHardwareBuffer(renderDevice);

	Nz::CommandBufferPtr drawCommandBuffer;
	auto RebuildCommandBuffer = [&]
	{
		Nz::Vector2ui windowSize = window.GetSize();

		drawCommandBuffer = commandPool->BuildCommandBuffer([&](Nz::CommandBufferBuilder& builder)
		{
			Nz::Recti renderRect(0, 0, window.GetSize().x, window.GetSize().y);

			Nz::CommandBufferBuilder::ClearValues clearValues[2];
			clearValues[0].color = Nz::Color::Black;
			clearValues[1].depth = 1.f;
			clearValues[1].stencil = 0;

			builder.BeginDebugRegion("Main window rendering", Nz::Color::Green);
			{
				builder.BeginRenderPass(windowImpl->GetFramebuffer(), windowImpl->GetRenderPass(), renderRect, { clearValues[0], clearValues[1] });
				{
					builder.BindIndexBuffer(indexBufferImpl);
					builder.BindPipeline(*pipeline);
					builder.BindVertexBuffer(0, vertexBufferImpl);
					builder.BindShaderBinding(*shaderBinding);

					builder.SetScissor(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) });
					builder.SetViewport(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) });

					builder.DrawIndexed(drfreakIB->GetIndexCount());
				}
				builder.EndRenderPass();
			}
			builder.EndDebugRegion();
		});
	};
	RebuildCommandBuffer();

	Nz::Vector3f viewerPos = Nz::Vector3f::Zero();

	Nz::EulerAnglesf camAngles(0.f, 0.f, 0.f);
	Nz::Quaternionf camQuat(camAngles);

	window.EnableEventPolling(true);

	Nz::Clock updateClock;
	Nz::Clock secondClock;
	unsigned int fps = 0;
	bool uboUpdate = true;

	//Nz::Mouse::SetRelativeMouseMode(true);

	while (window.IsOpen())
	{
		Nz::WindowEvent event;
		while (window.PollEvent(&event))
		{
			switch (event.type)
			{
				case Nz::WindowEventType_Quit:
					window.Close();
					break;

				/*case Nz::WindowEventType_MouseMoved: // La souris a bougé
				{
					// Gestion de la caméra free-fly (Rotation)
					float sensitivity = 0.3f; // Sensibilité de la souris

					// On modifie l'angle de la caméra grâce au déplacement relatif sur X de la souris
					camAngles.yaw = Nz::NormalizeAngle(camAngles.yaw - event.mouseMove.deltaX*sensitivity);

					// Idem, mais pour éviter les problèmes de calcul de la matrice de vue, on restreint les angles
					camAngles.pitch = Nz::Clamp(camAngles.pitch + event.mouseMove.deltaY*sensitivity, -89.f, 89.f);

					camQuat = camAngles;

					uboUpdate = true;
					break;
				}*/

				case Nz::WindowEventType_Resized:
				{
					Nz::Vector2ui windowSize = window.GetSize();
					ubo.projectionMatrix = Nz::Matrix4f::Perspective(70.f, float(windowSize.x) / windowSize.y, 0.1f, 1000.f);
					uboUpdate = true;
					break;
				}

				default:
					break;
			}
		}

		if (updateClock.GetMilliseconds() > 1000 / 60)
		{
			float cameraSpeed = 2.f * updateClock.GetSeconds();
			updateClock.Restart();

			if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Up) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Z))
				viewerPos += camQuat * Nz::Vector3f::Forward() * cameraSpeed;

			// Si la flèche du bas ou la touche S est pressée, on recule
			if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Down) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::S))
				viewerPos += camQuat * Nz::Vector3f::Backward() * cameraSpeed;

			// Etc...
			if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Left) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Q))
				viewerPos += camQuat * Nz::Vector3f::Left() * cameraSpeed;

			// Etc...
			if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Right) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::D))
				viewerPos += camQuat * Nz::Vector3f::Right() * cameraSpeed;

			// Majuscule pour monter, notez l'utilisation d'une direction globale (Non-affectée par la rotation)
			if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::LShift) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::RShift))
				viewerPos += Nz::Vector3f::Up() * cameraSpeed;

			// Contrôle (Gauche ou droite) pour descendre dans l'espace global, etc...
			if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::LControl) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::RControl))
				viewerPos += Nz::Vector3f::Down() * cameraSpeed;

			uboUpdate = true;
		}

		Nz::RenderFrame frame = windowImpl->Acquire();
		if (!frame)
			continue;

		if (frame.IsFramebufferInvalidated())
			RebuildCommandBuffer();

		ubo.viewMatrix = Nz::Matrix4f::ViewMatrix(viewerPos, camAngles);

		if (uboUpdate)
		{
			auto& allocation = frame.GetUploadPool().Allocate(uniformSize);

			std::memcpy(allocation.mappedPtr, &ubo, sizeof(ubo));

			frame.Execute([&](Nz::CommandBufferBuilder& builder)
			{
				builder.BeginDebugRegion("UBO Update", Nz::Color::Yellow);
				{
					builder.PreTransferBarrier();
					builder.CopyBuffer(allocation, uniformBuffer.get());
					builder.PostTransferBarrier();
				}
				builder.EndDebugRegion();
			}, Nz::QueueType::Transfer);

			uboUpdate = false;
		}

		frame.SubmitCommandBuffer(drawCommandBuffer.get(), Nz::QueueType::Graphics);

		frame.Present();

		window.Display();

		// On incrémente le compteur de FPS improvisé
		fps++;

		if (secondClock.GetMilliseconds() >= 1000) // Toutes les secondes
		{
			// Et on insère ces données dans le titre de la fenêtre
			window.SetTitle(windowTitle + " - " + Nz::String::Number(fps) + " FPS");

			/*
			Note: En C++11 il est possible d'insérer de l'Unicode de façon standard, quel que soit l'encodage du fichier,
			via quelque chose de similaire à u8"Cha\u00CEne de caract\u00E8res".
			Cependant, si le code source est encodé en UTF-8 (Comme c'est le cas dans ce fichier),
			cela fonctionnera aussi comme ceci : "Chaîne de caractères".
			*/

			// Et on réinitialise le compteur de FPS
			fps = 0;

			// Et on relance l'horloge pour refaire ça dans une seconde
			secondClock.Restart();
		}
	}

	return EXIT_SUCCESS;
}
<commit_msg>RenderTest: Make UBO Dynamic<commit_after>#include <Nazara/Utility.hpp>
#include <Nazara/Renderer.hpp>
#include <Nazara/Shader.hpp>
#include <Nazara/Shader/SpirvConstantCache.hpp>
#include <Nazara/Shader/SpirvPrinter.hpp>
#include <array>
#include <iostream>

int main()
{
	Nz::Initializer<Nz::Renderer> loader;
	if (!loader)
	{
		std::cout << "Failed to initialize Vulkan" << std::endl;
		return __LINE__;
	}

	Nz::RenderWindow window;

	Nz::MeshParams meshParams;
	meshParams.matrix = Nz::Matrix4f::Rotate(Nz::EulerAnglesf(0.f, 90.f, 180.f)) * Nz::Matrix4f::Scale(Nz::Vector3f(0.002f));
	meshParams.vertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout_XYZ_Normal_UV);

	Nz::String windowTitle = "Vulkan Test";
	if (!window.Create(Nz::VideoMode(800, 600, 32), windowTitle))
	{
		std::cout << "Failed to create Window" << std::endl;
		return __LINE__;
	}

	std::shared_ptr<Nz::RenderDevice> device = window.GetRenderDevice();

	auto fragmentShader = device->InstantiateShaderStage(Nz::ShaderStageType::Fragment, Nz::ShaderLanguage::NazaraBinary, "frag.shader");
	if (!fragmentShader)
	{
		std::cout << "Failed to instantiate fragment shader" << std::endl;
		return __LINE__;
	}

	auto vertexShader = device->InstantiateShaderStage(Nz::ShaderStageType::Vertex, Nz::ShaderLanguage::NazaraBinary, "vert.shader");
	if (!vertexShader)
	{
		std::cout << "Failed to instantiate fragment shader" << std::endl;
		return __LINE__;
	}

	Nz::MeshRef drfreak = Nz::Mesh::LoadFromFile("resources/Spaceship/spaceship.obj", meshParams);

	if (!drfreak)
	{
		NazaraError("Failed to load model");
		return __LINE__;
	}

	Nz::StaticMesh* drfreakMesh = static_cast<Nz::StaticMesh*>(drfreak->GetSubMesh(0));

	const Nz::VertexBuffer* drfreakVB = drfreakMesh->GetVertexBuffer();
	const Nz::IndexBuffer* drfreakIB = drfreakMesh->GetIndexBuffer();

	// Index buffer
	std::cout << "Index count: " << drfreakIB->GetIndexCount() << std::endl;

	// Vertex buffer
	std::cout << "Vertex count: " << drfreakVB->GetVertexCount() << std::endl;

	// Texture
	Nz::ImageRef drfreakImage = Nz::Image::LoadFromFile("resources/Spaceship/Texture/diffuse.png");
	if (!drfreakImage || !drfreakImage->Convert(Nz::PixelFormat_RGBA8))
	{
		NazaraError("Failed to load image");
		return __LINE__;
	}

	Nz::TextureInfo texParams;
	texParams.pixelFormat = drfreakImage->GetFormat();
	texParams.type = drfreakImage->GetType();
	texParams.width = drfreakImage->GetWidth();
	texParams.height = drfreakImage->GetHeight();
	texParams.depth = drfreakImage->GetDepth();

	std::unique_ptr<Nz::Texture> texture = device->InstantiateTexture(texParams);
	if (!texture->Update(drfreakImage->GetConstPixels()))
	{
		NazaraError("Failed to update texture");
		return __LINE__;
	}

	std::unique_ptr<Nz::TextureSampler> textureSampler = device->InstantiateTextureSampler({});

	struct
	{
		Nz::Matrix4f projectionMatrix;
		Nz::Matrix4f modelMatrix;
		Nz::Matrix4f viewMatrix;
	}
	ubo;

	Nz::Vector2ui windowSize = window.GetSize();
	ubo.projectionMatrix = Nz::Matrix4f::Perspective(70.f, float(windowSize.x) / windowSize.y, 0.1f, 1000.f);
	ubo.viewMatrix = Nz::Matrix4f::Translate(Nz::Vector3f::Backward() * 1);
	ubo.modelMatrix = Nz::Matrix4f::Translate(Nz::Vector3f::Forward() * 2 + Nz::Vector3f::Right());

	Nz::UInt32 uniformSize = sizeof(ubo);

	Nz::RenderPipelineLayoutInfo pipelineLayoutInfo;
	auto& uboBinding = pipelineLayoutInfo.bindings.emplace_back();
	uboBinding.index = 0;
	uboBinding.shaderStageFlags = Nz::ShaderStageType::Vertex;
	uboBinding.type = Nz::ShaderBindingType::UniformBuffer;

	auto& textureBinding = pipelineLayoutInfo.bindings.emplace_back();
	textureBinding.index = 1;
	textureBinding.shaderStageFlags = Nz::ShaderStageType::Fragment;
	textureBinding.type = Nz::ShaderBindingType::Texture;

	std::shared_ptr<Nz::RenderPipelineLayout> renderPipelineLayout = device->InstantiateRenderPipelineLayout(std::move(pipelineLayoutInfo));

	Nz::ShaderBindingPtr shaderBinding = renderPipelineLayout->AllocateShaderBinding();

	std::unique_ptr<Nz::AbstractBuffer> uniformBuffer = device->InstantiateBuffer(Nz::BufferType_Uniform);
	if (!uniformBuffer->Initialize(uniformSize, Nz::BufferUsage_DeviceLocal | Nz::BufferUsage_Dynamic))
	{
		NazaraError("Failed to create uniform buffer");
		return __LINE__;
	}

	shaderBinding->Update({
		{
			0,
			Nz::ShaderBinding::UniformBufferBinding {
				uniformBuffer.get(), 0, uniformSize
			}
		},
		{
			1,
			Nz::ShaderBinding::TextureBinding {
				texture.get(), textureSampler.get()
			}
		}
	});

	Nz::RenderPipelineInfo pipelineInfo;
	pipelineInfo.pipelineLayout = renderPipelineLayout;

	pipelineInfo.depthBuffer = true;
	pipelineInfo.shaderStages.emplace_back(fragmentShader);
	pipelineInfo.shaderStages.emplace_back(vertexShader);

	auto& vertexBuffer = pipelineInfo.vertexBuffers.emplace_back();
	vertexBuffer.binding = 0;
	vertexBuffer.declaration = drfreakVB->GetVertexDeclaration();

	std::unique_ptr<Nz::RenderPipeline> pipeline = device->InstantiateRenderPipeline(pipelineInfo);

	Nz::RenderDevice* renderDevice = window.GetRenderDevice().get();

	Nz::RenderWindowImpl* windowImpl = window.GetImpl();
	std::unique_ptr<Nz::CommandPool> commandPool = windowImpl->CreateCommandPool(Nz::QueueType::Graphics);

	Nz::RenderBuffer* renderBufferIB = static_cast<Nz::RenderBuffer*>(drfreakIB->GetBuffer()->GetImpl());
	Nz::RenderBuffer* renderBufferVB = static_cast<Nz::RenderBuffer*>(drfreakVB->GetBuffer()->GetImpl());

	if (!renderBufferIB->Synchronize(renderDevice))
	{
		NazaraError("Failed to synchronize render buffer");
		return __LINE__;
	}

	if (!renderBufferVB->Synchronize(renderDevice))
	{
		NazaraError("Failed to synchronize render buffer");
		return __LINE__;
	}

	Nz::AbstractBuffer* indexBufferImpl = renderBufferIB->GetHardwareBuffer(renderDevice);
	Nz::AbstractBuffer* vertexBufferImpl = renderBufferVB->GetHardwareBuffer(renderDevice);

	Nz::CommandBufferPtr drawCommandBuffer;
	auto RebuildCommandBuffer = [&]
	{
		Nz::Vector2ui windowSize = window.GetSize();

		drawCommandBuffer = commandPool->BuildCommandBuffer([&](Nz::CommandBufferBuilder& builder)
		{
			Nz::Recti renderRect(0, 0, window.GetSize().x, window.GetSize().y);

			Nz::CommandBufferBuilder::ClearValues clearValues[2];
			clearValues[0].color = Nz::Color::Black;
			clearValues[1].depth = 1.f;
			clearValues[1].stencil = 0;

			builder.BeginDebugRegion("Main window rendering", Nz::Color::Green);
			{
				builder.BeginRenderPass(windowImpl->GetFramebuffer(), windowImpl->GetRenderPass(), renderRect, { clearValues[0], clearValues[1] });
				{
					builder.BindIndexBuffer(indexBufferImpl);
					builder.BindPipeline(*pipeline);
					builder.BindVertexBuffer(0, vertexBufferImpl);
					builder.BindShaderBinding(*shaderBinding);

					builder.SetScissor(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) });
					builder.SetViewport(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) });

					builder.DrawIndexed(drfreakIB->GetIndexCount());
				}
				builder.EndRenderPass();
			}
			builder.EndDebugRegion();
		});
	};
	RebuildCommandBuffer();

	Nz::Vector3f viewerPos = Nz::Vector3f::Zero();

	Nz::EulerAnglesf camAngles(0.f, 0.f, 0.f);
	Nz::Quaternionf camQuat(camAngles);

	window.EnableEventPolling(true);

	Nz::Clock updateClock;
	Nz::Clock secondClock;
	unsigned int fps = 0;
	bool uboUpdate = true;

	//Nz::Mouse::SetRelativeMouseMode(true);

	while (window.IsOpen())
	{
		Nz::WindowEvent event;
		while (window.PollEvent(&event))
		{
			switch (event.type)
			{
				case Nz::WindowEventType_Quit:
					window.Close();
					break;

				/*case Nz::WindowEventType_MouseMoved: // La souris a bougé
				{
					// Gestion de la caméra free-fly (Rotation)
					float sensitivity = 0.3f; // Sensibilité de la souris

					// On modifie l'angle de la caméra grâce au déplacement relatif sur X de la souris
					camAngles.yaw = Nz::NormalizeAngle(camAngles.yaw - event.mouseMove.deltaX*sensitivity);

					// Idem, mais pour éviter les problèmes de calcul de la matrice de vue, on restreint les angles
					camAngles.pitch = Nz::Clamp(camAngles.pitch + event.mouseMove.deltaY*sensitivity, -89.f, 89.f);

					camQuat = camAngles;

					uboUpdate = true;
					break;
				}*/

				case Nz::WindowEventType_Resized:
				{
					Nz::Vector2ui windowSize = window.GetSize();
					ubo.projectionMatrix = Nz::Matrix4f::Perspective(70.f, float(windowSize.x) / windowSize.y, 0.1f, 1000.f);
					uboUpdate = true;
					break;
				}

				default:
					break;
			}
		}

		if (updateClock.GetMilliseconds() > 1000 / 60)
		{
			float cameraSpeed = 2.f * updateClock.GetSeconds();
			updateClock.Restart();

			if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Up) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Z))
				viewerPos += camQuat * Nz::Vector3f::Forward() * cameraSpeed;

			// Si la flèche du bas ou la touche S est pressée, on recule
			if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Down) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::S))
				viewerPos += camQuat * Nz::Vector3f::Backward() * cameraSpeed;

			// Etc...
			if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Left) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Q))
				viewerPos += camQuat * Nz::Vector3f::Left() * cameraSpeed;

			// Etc...
			if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Right) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::D))
				viewerPos += camQuat * Nz::Vector3f::Right() * cameraSpeed;

			// Majuscule pour monter, notez l'utilisation d'une direction globale (Non-affectée par la rotation)
			if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::LShift) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::RShift))
				viewerPos += Nz::Vector3f::Up() * cameraSpeed;

			// Contrôle (Gauche ou droite) pour descendre dans l'espace global, etc...
			if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::LControl) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::RControl))
				viewerPos += Nz::Vector3f::Down() * cameraSpeed;

			uboUpdate = true;
		}

		Nz::RenderFrame frame = windowImpl->Acquire();
		if (!frame)
			continue;

		if (frame.IsFramebufferInvalidated())
			RebuildCommandBuffer();

		ubo.viewMatrix = Nz::Matrix4f::ViewMatrix(viewerPos, camAngles);

		if (uboUpdate)
		{
			auto& allocation = frame.GetUploadPool().Allocate(uniformSize);

			std::memcpy(allocation.mappedPtr, &ubo, sizeof(ubo));

			frame.Execute([&](Nz::CommandBufferBuilder& builder)
			{
				builder.BeginDebugRegion("UBO Update", Nz::Color::Yellow);
				{
					builder.PreTransferBarrier();
					builder.CopyBuffer(allocation, uniformBuffer.get());
					builder.PostTransferBarrier();
				}
				builder.EndDebugRegion();
			}, Nz::QueueType::Transfer);

			uboUpdate = false;
		}

		frame.SubmitCommandBuffer(drawCommandBuffer.get(), Nz::QueueType::Graphics);

		frame.Present();

		window.Display();

		// On incrémente le compteur de FPS improvisé
		fps++;

		if (secondClock.GetMilliseconds() >= 1000) // Toutes les secondes
		{
			// Et on insère ces données dans le titre de la fenêtre
			window.SetTitle(windowTitle + " - " + Nz::String::Number(fps) + " FPS");

			/*
			Note: En C++11 il est possible d'insérer de l'Unicode de façon standard, quel que soit l'encodage du fichier,
			via quelque chose de similaire à u8"Cha\u00CEne de caract\u00E8res".
			Cependant, si le code source est encodé en UTF-8 (Comme c'est le cas dans ce fichier),
			cela fonctionnera aussi comme ceci : "Chaîne de caractères".
			*/

			// Et on réinitialise le compteur de FPS
			fps = 0;

			// Et on relance l'horloge pour refaire ça dans une seconde
			secondClock.Restart();
		}
	}

	return EXIT_SUCCESS;
}
<|endoftext|>