mirror of
https://github.com/RGBCube/serenity
synced 2025-07-28 13:47:46 +00:00
DevTools: Move to Userland/DevTools/
This commit is contained in:
parent
13d7c09125
commit
4055b03291
125 changed files with 2 additions and 2 deletions
10
Userland/DevTools/Inspector/CMakeLists.txt
Normal file
10
Userland/DevTools/Inspector/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
set(SOURCES
|
||||
main.cpp
|
||||
RemoteObject.cpp
|
||||
RemoteObjectGraphModel.cpp
|
||||
RemoteObjectPropertyModel.cpp
|
||||
RemoteProcess.cpp
|
||||
)
|
||||
|
||||
serenity_app(Inspector ICON app-inspector)
|
||||
target_link_libraries(Inspector LibGUI)
|
43
Userland/DevTools/Inspector/RemoteObject.cpp
Normal file
43
Userland/DevTools/Inspector/RemoteObject.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "RemoteObject.h"
|
||||
#include "RemoteObjectPropertyModel.h"
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
RemoteObject::RemoteObject()
|
||||
: m_property_model(RemoteObjectPropertyModel::create(*this))
|
||||
{
|
||||
}
|
||||
|
||||
RemoteObjectPropertyModel& RemoteObject::property_model()
|
||||
{
|
||||
m_property_model->update();
|
||||
return *m_property_model;
|
||||
}
|
||||
|
||||
}
|
57
Userland/DevTools/Inspector/RemoteObject.h
Normal file
57
Userland/DevTools/Inspector/RemoteObject.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/JsonObject.h>
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
class RemoteObjectPropertyModel;
|
||||
|
||||
class RemoteObject {
|
||||
public:
|
||||
RemoteObject();
|
||||
|
||||
RemoteObjectPropertyModel& property_model();
|
||||
|
||||
RemoteObject* parent { nullptr };
|
||||
NonnullOwnPtrVector<RemoteObject> children;
|
||||
|
||||
FlatPtr address { 0 };
|
||||
FlatPtr parent_address { 0 };
|
||||
String class_name;
|
||||
String name;
|
||||
|
||||
JsonObject json;
|
||||
|
||||
NonnullRefPtr<RemoteObjectPropertyModel> m_property_model;
|
||||
};
|
||||
|
||||
}
|
124
Userland/DevTools/Inspector/RemoteObjectGraphModel.cpp
Normal file
124
Userland/DevTools/Inspector/RemoteObjectGraphModel.cpp
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "RemoteObjectGraphModel.h"
|
||||
#include "RemoteObject.h"
|
||||
#include "RemoteProcess.h"
|
||||
#include <AK/JsonObject.h>
|
||||
#include <AK/JsonValue.h>
|
||||
#include <LibGUI/Application.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
RemoteObjectGraphModel::RemoteObjectGraphModel(RemoteProcess& process)
|
||||
: m_process(process)
|
||||
{
|
||||
m_object_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png"));
|
||||
m_window_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/window.png"));
|
||||
m_layout_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/layout.png"));
|
||||
m_timer_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/timer.png"));
|
||||
}
|
||||
|
||||
RemoteObjectGraphModel::~RemoteObjectGraphModel()
|
||||
{
|
||||
}
|
||||
|
||||
GUI::ModelIndex RemoteObjectGraphModel::index(int row, int column, const GUI::ModelIndex& parent) const
|
||||
{
|
||||
if (!parent.is_valid()) {
|
||||
if (m_process.roots().is_empty())
|
||||
return {};
|
||||
return create_index(row, column, &m_process.roots().at(row));
|
||||
}
|
||||
auto& remote_parent = *static_cast<RemoteObject*>(parent.internal_data());
|
||||
return create_index(row, column, &remote_parent.children.at(row));
|
||||
}
|
||||
|
||||
GUI::ModelIndex RemoteObjectGraphModel::parent_index(const GUI::ModelIndex& index) const
|
||||
{
|
||||
if (!index.is_valid())
|
||||
return {};
|
||||
auto& remote_object = *static_cast<RemoteObject*>(index.internal_data());
|
||||
if (!remote_object.parent)
|
||||
return {};
|
||||
|
||||
// NOTE: If the parent has no parent, it's a root, so we have to look among the remote roots.
|
||||
if (!remote_object.parent->parent) {
|
||||
for (size_t row = 0; row < m_process.roots().size(); ++row) {
|
||||
if (&m_process.roots()[row] == remote_object.parent)
|
||||
return create_index(row, 0, remote_object.parent);
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
return {};
|
||||
}
|
||||
|
||||
for (size_t row = 0; row < remote_object.parent->parent->children.size(); ++row) {
|
||||
if (&remote_object.parent->parent->children[row] == remote_object.parent)
|
||||
return create_index(row, 0, remote_object.parent);
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
return {};
|
||||
}
|
||||
|
||||
int RemoteObjectGraphModel::row_count(const GUI::ModelIndex& index) const
|
||||
{
|
||||
if (!index.is_valid())
|
||||
return m_process.roots().size();
|
||||
auto& remote_object = *static_cast<RemoteObject*>(index.internal_data());
|
||||
return remote_object.children.size();
|
||||
}
|
||||
|
||||
int RemoteObjectGraphModel::column_count(const GUI::ModelIndex&) const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
GUI::Variant RemoteObjectGraphModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
|
||||
{
|
||||
auto* remote_object = static_cast<RemoteObject*>(index.internal_data());
|
||||
if (role == GUI::ModelRole::Icon) {
|
||||
if (remote_object->class_name == "Window")
|
||||
return m_window_icon;
|
||||
if (remote_object->class_name == "Timer")
|
||||
return m_timer_icon;
|
||||
if (remote_object->class_name.ends_with("Layout"))
|
||||
return m_layout_icon;
|
||||
return m_object_icon;
|
||||
}
|
||||
if (role == GUI::ModelRole::Display)
|
||||
return String::formatted("{}({:p})", remote_object->class_name, remote_object->address);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void RemoteObjectGraphModel::update()
|
||||
{
|
||||
did_update();
|
||||
}
|
||||
|
||||
}
|
66
Userland/DevTools/Inspector/RemoteObjectGraphModel.h
Normal file
66
Userland/DevTools/Inspector/RemoteObjectGraphModel.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/JsonArray.h>
|
||||
#include <AK/JsonObject.h>
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <LibCore/LocalSocket.h>
|
||||
#include <LibGUI/Model.h>
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
class RemoteProcess;
|
||||
|
||||
class RemoteObjectGraphModel final : public GUI::Model {
|
||||
public:
|
||||
static NonnullRefPtr<RemoteObjectGraphModel> create(RemoteProcess& process)
|
||||
{
|
||||
return adopt(*new RemoteObjectGraphModel(process));
|
||||
}
|
||||
|
||||
virtual ~RemoteObjectGraphModel() override;
|
||||
|
||||
virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
|
||||
virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
|
||||
virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override;
|
||||
virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override;
|
||||
virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override;
|
||||
virtual void update() override;
|
||||
|
||||
private:
|
||||
explicit RemoteObjectGraphModel(RemoteProcess&);
|
||||
|
||||
RemoteProcess& m_process;
|
||||
|
||||
GUI::Icon m_object_icon;
|
||||
GUI::Icon m_window_icon;
|
||||
GUI::Icon m_layout_icon;
|
||||
GUI::Icon m_timer_icon;
|
||||
};
|
||||
|
||||
}
|
241
Userland/DevTools/Inspector/RemoteObjectPropertyModel.cpp
Normal file
241
Userland/DevTools/Inspector/RemoteObjectPropertyModel.cpp
Normal file
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "RemoteObjectPropertyModel.h"
|
||||
#include "RemoteObject.h"
|
||||
#include "RemoteProcess.h"
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
RemoteObjectPropertyModel::RemoteObjectPropertyModel(RemoteObject& object)
|
||||
: m_object(object)
|
||||
{
|
||||
}
|
||||
|
||||
int RemoteObjectPropertyModel::row_count(const GUI::ModelIndex& index) const
|
||||
{
|
||||
Function<int(const JsonValue&)> do_count = [&](const JsonValue& value) {
|
||||
if (value.is_array())
|
||||
return value.as_array().size();
|
||||
else if (value.is_object())
|
||||
return value.as_object().size();
|
||||
return 0;
|
||||
};
|
||||
|
||||
if (index.is_valid()) {
|
||||
auto* path = static_cast<const JsonPath*>(index.internal_data());
|
||||
return do_count(path->resolve(m_object.json));
|
||||
} else {
|
||||
return do_count(m_object.json);
|
||||
}
|
||||
}
|
||||
|
||||
String RemoteObjectPropertyModel::column_name(int column) const
|
||||
{
|
||||
switch (column) {
|
||||
case Column::Name:
|
||||
return "Name";
|
||||
case Column::Value:
|
||||
return "Value";
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
GUI::Variant RemoteObjectPropertyModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
|
||||
{
|
||||
auto* path = static_cast<const JsonPath*>(index.internal_data());
|
||||
if (!path)
|
||||
return {};
|
||||
|
||||
if (role == GUI::ModelRole::Display) {
|
||||
switch (index.column()) {
|
||||
case Column::Name:
|
||||
return path->last().to_string();
|
||||
case Column::Value: {
|
||||
auto data = path->resolve(m_object.json);
|
||||
if (data.is_array())
|
||||
return String::formatted("<Array with {} element{}", data.as_array().size(), data.as_array().size() == 1 ? ">" : "s>");
|
||||
if (data.is_object())
|
||||
return String::formatted("<Object with {} entr{}", data.as_object().size(), data.as_object().size() == 1 ? "y>" : "ies>");
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void RemoteObjectPropertyModel::update()
|
||||
{
|
||||
did_update();
|
||||
}
|
||||
|
||||
void RemoteObjectPropertyModel::set_data(const GUI::ModelIndex& index, const GUI::Variant& new_value)
|
||||
{
|
||||
if (!index.is_valid())
|
||||
return;
|
||||
|
||||
auto* path = static_cast<const JsonPath*>(index.internal_data());
|
||||
if (path->size() != 1)
|
||||
return;
|
||||
|
||||
FlatPtr address = m_object.address;
|
||||
RemoteProcess::the().set_property(address, path->first().to_string(), new_value.to_string());
|
||||
did_update();
|
||||
}
|
||||
|
||||
GUI::ModelIndex RemoteObjectPropertyModel::index(int row, int column, const GUI::ModelIndex& parent) const
|
||||
{
|
||||
const auto& parent_path = parent.is_valid() ? *static_cast<const JsonPath*>(parent.internal_data()) : JsonPath {};
|
||||
|
||||
auto nth_child = [&](int n, const JsonValue& value) -> const JsonPath* {
|
||||
auto path = make<JsonPath>();
|
||||
path->append(parent_path);
|
||||
int row_index = n;
|
||||
if (value.is_object()) {
|
||||
String property_name;
|
||||
auto& object = value.as_object();
|
||||
object.for_each_member([&](auto& name, auto&) {
|
||||
if (row_index > 0) {
|
||||
--row_index;
|
||||
} else if (row_index == 0) {
|
||||
property_name = name;
|
||||
--row_index;
|
||||
}
|
||||
});
|
||||
if (property_name.is_null())
|
||||
return nullptr;
|
||||
|
||||
path->append({ property_name });
|
||||
m_paths.append(move(path));
|
||||
} else if (value.is_array()) {
|
||||
path->append(JsonPathElement { (size_t)n });
|
||||
m_paths.append(move(path));
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
return &m_paths.last();
|
||||
};
|
||||
|
||||
if (!parent.is_valid()) {
|
||||
if (m_object.json.is_empty())
|
||||
return {};
|
||||
}
|
||||
|
||||
auto index_path = cached_path_at(row, parent_path);
|
||||
|
||||
if (!index_path)
|
||||
index_path = nth_child(row, parent_path.resolve(m_object.json));
|
||||
|
||||
if (!index_path)
|
||||
return {};
|
||||
|
||||
return create_index(row, column, index_path);
|
||||
}
|
||||
|
||||
GUI::ModelIndex RemoteObjectPropertyModel::parent_index(const GUI::ModelIndex& index) const
|
||||
{
|
||||
if (!index.is_valid())
|
||||
return index;
|
||||
|
||||
auto path = *static_cast<const JsonPath*>(index.internal_data());
|
||||
if (path.is_empty())
|
||||
return {};
|
||||
|
||||
path.take_last();
|
||||
if (path.is_empty())
|
||||
return {};
|
||||
|
||||
auto* cpath = find_cached_path(path);
|
||||
if (cpath) {
|
||||
int index_in_parent = 0;
|
||||
if (cpath->last().kind() == JsonPathElement::Kind::Index)
|
||||
index_in_parent = cpath->last().index();
|
||||
else if (cpath->last().kind() == JsonPathElement::Kind::Key) {
|
||||
auto path_copy = path;
|
||||
auto last = path_copy.take_last();
|
||||
bool found = false;
|
||||
path_copy.resolve(m_object.json).as_object().for_each_member([&](auto& name, auto&) {
|
||||
if (!found) {
|
||||
if (last.key() == name)
|
||||
found = true;
|
||||
else
|
||||
index_in_parent++;
|
||||
}
|
||||
});
|
||||
}
|
||||
return create_index(index_in_parent, 0, cpath);
|
||||
}
|
||||
|
||||
dbgln("No cached path found for path {}", path.to_string());
|
||||
return {};
|
||||
}
|
||||
|
||||
const JsonPath* RemoteObjectPropertyModel::cached_path_at(int n, const Vector<JsonPathElement>& prefix) const
|
||||
{
|
||||
// FIXME: ModelIndex wants a void*, so we have to keep these
|
||||
// indices alive, but allocating a new path every time
|
||||
// we're asked for an index is silly, so we have to look for existing ones first.
|
||||
const JsonPath* index_path = nullptr;
|
||||
int row_index = n;
|
||||
for (auto& path : m_paths) {
|
||||
if (path.size() != prefix.size() + 1)
|
||||
continue;
|
||||
|
||||
for (size_t i = 0; i < prefix.size(); ++i) {
|
||||
if (path[i] != prefix[i])
|
||||
goto do_continue;
|
||||
}
|
||||
|
||||
if (row_index == 0) {
|
||||
index_path = &path;
|
||||
break;
|
||||
}
|
||||
--row_index;
|
||||
do_continue:;
|
||||
}
|
||||
|
||||
return index_path;
|
||||
};
|
||||
|
||||
const JsonPath* RemoteObjectPropertyModel::find_cached_path(const Vector<JsonPathElement>& path) const
|
||||
{
|
||||
for (auto& cpath : m_paths) {
|
||||
if (cpath.size() != path.size())
|
||||
continue;
|
||||
|
||||
for (size_t i = 0; i < cpath.size(); ++i) {
|
||||
if (cpath[i] != path[i])
|
||||
goto do_continue;
|
||||
}
|
||||
|
||||
return &cpath;
|
||||
do_continue:;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
72
Userland/DevTools/Inspector/RemoteObjectPropertyModel.h
Normal file
72
Userland/DevTools/Inspector/RemoteObjectPropertyModel.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/JsonPath.h>
|
||||
#include <AK/JsonValue.h>
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <LibGUI/Model.h>
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
class RemoteObject;
|
||||
|
||||
class RemoteObjectPropertyModel final : public GUI::Model {
|
||||
public:
|
||||
virtual ~RemoteObjectPropertyModel() override { }
|
||||
static NonnullRefPtr<RemoteObjectPropertyModel> create(RemoteObject& object)
|
||||
{
|
||||
return adopt(*new RemoteObjectPropertyModel(object));
|
||||
}
|
||||
|
||||
enum Column {
|
||||
Name,
|
||||
Value,
|
||||
__Count,
|
||||
};
|
||||
|
||||
virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
|
||||
virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return Column::__Count; }
|
||||
virtual String column_name(int) const override;
|
||||
virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override;
|
||||
virtual void set_data(const GUI::ModelIndex&, const GUI::Variant&) override;
|
||||
virtual void update() override;
|
||||
virtual bool is_editable(const GUI::ModelIndex& index) const override { return index.column() == Column::Value; }
|
||||
virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override;
|
||||
virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override;
|
||||
|
||||
private:
|
||||
explicit RemoteObjectPropertyModel(RemoteObject&);
|
||||
|
||||
const JsonPath* cached_path_at(int n, const Vector<JsonPathElement>& prefix) const;
|
||||
const JsonPath* find_cached_path(const Vector<JsonPathElement>& path) const;
|
||||
|
||||
RemoteObject& m_object;
|
||||
mutable NonnullOwnPtrVector<JsonPath> m_paths;
|
||||
};
|
||||
|
||||
}
|
201
Userland/DevTools/Inspector/RemoteProcess.cpp
Normal file
201
Userland/DevTools/Inspector/RemoteProcess.cpp
Normal file
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "RemoteProcess.h"
|
||||
#include "RemoteObject.h"
|
||||
#include "RemoteObjectGraphModel.h"
|
||||
#include "RemoteObjectPropertyModel.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
RemoteProcess* s_the;
|
||||
|
||||
RemoteProcess& RemoteProcess::the()
|
||||
{
|
||||
return *s_the;
|
||||
}
|
||||
|
||||
RemoteProcess::RemoteProcess(pid_t pid)
|
||||
: m_pid(pid)
|
||||
, m_object_graph_model(RemoteObjectGraphModel::create(*this))
|
||||
, m_socket(Core::LocalSocket::construct())
|
||||
{
|
||||
s_the = this;
|
||||
m_socket->set_blocking(true);
|
||||
}
|
||||
|
||||
void RemoteProcess::handle_identify_response(const JsonObject& response)
|
||||
{
|
||||
int pid = response.get("pid").to_int();
|
||||
ASSERT(pid == m_pid);
|
||||
|
||||
m_process_name = response.get("process_name").as_string_or({});
|
||||
|
||||
if (on_update)
|
||||
on_update();
|
||||
}
|
||||
|
||||
void RemoteProcess::handle_get_all_objects_response(const JsonObject& response)
|
||||
{
|
||||
// FIXME: It would be good if we didn't have to make a local copy of the array value here!
|
||||
auto objects = response.get("objects");
|
||||
auto& object_array = objects.as_array();
|
||||
|
||||
NonnullOwnPtrVector<RemoteObject> remote_objects;
|
||||
HashMap<FlatPtr, RemoteObject*> objects_by_address;
|
||||
|
||||
for (auto& value : object_array.values()) {
|
||||
ASSERT(value.is_object());
|
||||
auto& object = value.as_object();
|
||||
auto remote_object = make<RemoteObject>();
|
||||
remote_object->address = object.get("address").to_number<FlatPtr>();
|
||||
remote_object->parent_address = object.get("parent").to_number<FlatPtr>();
|
||||
remote_object->name = object.get("name").to_string();
|
||||
remote_object->class_name = object.get("class_name").to_string();
|
||||
remote_object->json = object;
|
||||
objects_by_address.set(remote_object->address, remote_object);
|
||||
remote_objects.append(move(remote_object));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < remote_objects.size(); ++i) {
|
||||
auto& remote_object = remote_objects.ptr_at(i);
|
||||
auto* parent = objects_by_address.get(remote_object->parent_address).value_or(nullptr);
|
||||
if (!parent) {
|
||||
m_roots.append(move(remote_object));
|
||||
} else {
|
||||
remote_object->parent = parent;
|
||||
parent->children.append(move(remote_object));
|
||||
}
|
||||
}
|
||||
|
||||
m_object_graph_model->update();
|
||||
|
||||
if (on_update)
|
||||
on_update();
|
||||
}
|
||||
|
||||
void RemoteProcess::send_request(const JsonObject& request)
|
||||
{
|
||||
auto serialized = request.to_string();
|
||||
i32 length = serialized.length();
|
||||
m_socket->write((const u8*)&length, sizeof(length));
|
||||
m_socket->write(serialized);
|
||||
}
|
||||
|
||||
void RemoteProcess::set_inspected_object(FlatPtr address)
|
||||
{
|
||||
JsonObject request;
|
||||
request.set("type", "SetInspectedObject");
|
||||
request.set("address", address);
|
||||
send_request(request);
|
||||
}
|
||||
|
||||
void RemoteProcess::set_property(FlatPtr object, const StringView& name, const JsonValue& value)
|
||||
{
|
||||
JsonObject request;
|
||||
request.set("type", "SetProperty");
|
||||
request.set("address", object);
|
||||
request.set("name", JsonValue(name));
|
||||
request.set("value", value);
|
||||
send_request(request);
|
||||
}
|
||||
|
||||
void RemoteProcess::update()
|
||||
{
|
||||
m_socket->on_connected = [this] {
|
||||
dbgln("Connected to PID {}", m_pid);
|
||||
|
||||
{
|
||||
JsonObject request;
|
||||
request.set("type", "Identify");
|
||||
send_request(request);
|
||||
}
|
||||
|
||||
{
|
||||
JsonObject request;
|
||||
request.set("type", "GetAllObjects");
|
||||
send_request(request);
|
||||
}
|
||||
};
|
||||
|
||||
m_socket->on_ready_to_read = [this] {
|
||||
if (m_socket->eof()) {
|
||||
dbgln("Disconnected from PID {}", m_pid);
|
||||
m_socket->close();
|
||||
return;
|
||||
}
|
||||
|
||||
u32 length;
|
||||
int nread = m_socket->read((u8*)&length, sizeof(length));
|
||||
ASSERT(nread == sizeof(length));
|
||||
|
||||
ByteBuffer data;
|
||||
size_t remaining_bytes = length;
|
||||
|
||||
while (remaining_bytes) {
|
||||
auto packet = m_socket->read(remaining_bytes);
|
||||
if (packet.size() == 0)
|
||||
break;
|
||||
data.append(packet.data(), packet.size());
|
||||
remaining_bytes -= packet.size();
|
||||
}
|
||||
|
||||
ASSERT(data.size() == length);
|
||||
dbgln("Got data size {} and read that many bytes", length);
|
||||
|
||||
auto json_value = JsonValue::from_string(data);
|
||||
ASSERT(json_value.has_value());
|
||||
ASSERT(json_value.value().is_object());
|
||||
|
||||
dbgln("Got JSON response {}", json_value.value());
|
||||
|
||||
auto& response = json_value.value().as_object();
|
||||
|
||||
auto response_type = response.get("type").as_string_or({});
|
||||
if (response_type.is_null())
|
||||
return;
|
||||
|
||||
if (response_type == "GetAllObjects") {
|
||||
handle_get_all_objects_response(response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (response_type == "Identify") {
|
||||
handle_identify_response(response);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
auto success = m_socket->connect(Core::SocketAddress::local(String::format("/tmp/rpc/%d", m_pid)));
|
||||
if (!success) {
|
||||
warnln("Couldn't connect to PID {}", m_pid);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
69
Userland/DevTools/Inspector/RemoteProcess.h
Normal file
69
Userland/DevTools/Inspector/RemoteProcess.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <LibCore/LocalSocket.h>
|
||||
|
||||
namespace Inspector {
|
||||
|
||||
class RemoteObjectGraphModel;
|
||||
class RemoteObject;
|
||||
|
||||
class RemoteProcess {
|
||||
public:
|
||||
static RemoteProcess& the();
|
||||
|
||||
explicit RemoteProcess(pid_t);
|
||||
void update();
|
||||
|
||||
pid_t pid() const { return m_pid; }
|
||||
const String& process_name() const { return m_process_name; }
|
||||
|
||||
RemoteObjectGraphModel& object_graph_model() { return *m_object_graph_model; }
|
||||
const NonnullOwnPtrVector<RemoteObject>& roots() const { return m_roots; }
|
||||
|
||||
void set_inspected_object(FlatPtr);
|
||||
|
||||
void set_property(FlatPtr object, const StringView& name, const JsonValue& value);
|
||||
|
||||
Function<void()> on_update;
|
||||
|
||||
private:
|
||||
void handle_get_all_objects_response(const AK::JsonObject&);
|
||||
void handle_identify_response(const AK::JsonObject&);
|
||||
|
||||
void send_request(const AK::JsonObject&);
|
||||
|
||||
pid_t m_pid { -1 };
|
||||
String m_process_name;
|
||||
NonnullRefPtr<RemoteObjectGraphModel> m_object_graph_model;
|
||||
RefPtr<Core::LocalSocket> m_socket;
|
||||
NonnullOwnPtrVector<RemoteObject> m_roots;
|
||||
};
|
||||
|
||||
}
|
152
Userland/DevTools/Inspector/main.cpp
Normal file
152
Userland/DevTools/Inspector/main.cpp
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "RemoteObject.h"
|
||||
#include "RemoteObjectGraphModel.h"
|
||||
#include "RemoteObjectPropertyModel.h"
|
||||
#include "RemoteProcess.h"
|
||||
#include <LibGUI/Application.h>
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/MenuBar.h>
|
||||
#include <LibGUI/ModelEditingDelegate.h>
|
||||
#include <LibGUI/ProcessChooser.h>
|
||||
#include <LibGUI/Splitter.h>
|
||||
#include <LibGUI/TreeView.h>
|
||||
#include <LibGUI/Window.h>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace Inspector;
|
||||
|
||||
[[noreturn]] static void print_usage_and_exit()
|
||||
{
|
||||
outln("usage: Inspector <pid>");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (pledge("stdio shared_buffer rpath accept unix cpath fattr", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil("/res", "r") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil("/bin", "r") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil("/tmp", "rwc") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil("/proc/all", "r") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil("/etc/passwd", "r") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
unveil(nullptr, nullptr);
|
||||
|
||||
pid_t pid;
|
||||
|
||||
auto app = GUI::Application::construct(argc, argv);
|
||||
auto app_icon = GUI::Icon::default_icon("app-inspector");
|
||||
if (argc != 2) {
|
||||
auto process_chooser = GUI::ProcessChooser::construct("Inspector", "Inspect", app_icon.bitmap_for_size(16));
|
||||
if (process_chooser->exec() == GUI::Dialog::ExecCancel)
|
||||
return 0;
|
||||
pid = process_chooser->pid();
|
||||
} else {
|
||||
auto pid_opt = String(argv[1]).to_int();
|
||||
if (!pid_opt.has_value())
|
||||
print_usage_and_exit();
|
||||
pid = pid_opt.value();
|
||||
}
|
||||
|
||||
auto window = GUI::Window::construct();
|
||||
window->set_title("Inspector");
|
||||
window->resize(685, 500);
|
||||
window->set_icon(app_icon.bitmap_for_size(16));
|
||||
|
||||
auto menubar = GUI::MenuBar::construct();
|
||||
auto& app_menu = menubar->add_menu("Inspector");
|
||||
app_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); }));
|
||||
|
||||
auto& help_menu = menubar->add_menu("Help");
|
||||
help_menu.add_action(GUI::CommonActions::make_about_action("Inspector", app_icon, window));
|
||||
|
||||
auto& widget = window->set_main_widget<GUI::Widget>();
|
||||
widget.set_fill_with_background_color(true);
|
||||
widget.set_layout<GUI::VerticalBoxLayout>();
|
||||
|
||||
auto& splitter = widget.add<GUI::HorizontalSplitter>();
|
||||
|
||||
RemoteProcess remote_process(pid);
|
||||
|
||||
remote_process.on_update = [&] {
|
||||
if (!remote_process.process_name().is_null())
|
||||
window->set_title(String::formatted("{} ({}) - Inspector", remote_process.process_name(), remote_process.pid()));
|
||||
};
|
||||
|
||||
auto& tree_view = splitter.add<GUI::TreeView>();
|
||||
tree_view.set_model(remote_process.object_graph_model());
|
||||
tree_view.set_activates_on_selection(true);
|
||||
tree_view.set_fixed_width(286);
|
||||
|
||||
auto& properties_tree_view = splitter.add<GUI::TreeView>();
|
||||
properties_tree_view.set_editable(true);
|
||||
properties_tree_view.aid_create_editing_delegate = [](auto&) {
|
||||
return make<GUI::StringModelEditingDelegate>();
|
||||
};
|
||||
|
||||
tree_view.on_activation = [&](auto& index) {
|
||||
auto* remote_object = static_cast<RemoteObject*>(index.internal_data());
|
||||
properties_tree_view.set_model(remote_object->property_model());
|
||||
remote_process.set_inspected_object(remote_object->address);
|
||||
};
|
||||
|
||||
app->set_menubar(move(menubar));
|
||||
window->show();
|
||||
remote_process.update();
|
||||
|
||||
if (pledge("stdio shared_buffer rpath accept unix", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return app->exec();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue