LINUX.ORG.RU

x11 кодировка

 , ,


0

1

Решил написать пет проект, time tracker, просто по фану.

Столкнулся с проблемой кодировки, раньше как-то удавалось обходить.

Проблема заключается в том что в консоли я вижу «кракозябры», подскажите какие изменения я могу внести в код, чтобы кирилица заработала?

Вот пример вывода программы:

konsole
        cmake-build-debug : att : 14s
        cmake-build-debug : zsh : 3s
        home : zsh : 3s


spotify
        Curtis Waters - Stunnin' : 9s
        Joji - Daylight : 8s
        Kino - Ðвезда по имени СолнÑе : 1s
        Kino - ÐаÑка ÑигаÑÐµÑ : 5s
        Kino - ХоÑÑ Ð¿ÐµÑемен : 5s


#include <X11/Xlib.h>
#include <X11/Xmu/WinUtil.h>

#include <iostream>
#include <chrono>
#include <thread>
#include <set>


struct ApplicationInfo
{
	struct TabInfo
	{
		friend bool operator<(const TabInfo& lhs, const TabInfo& rhs)
		{
			return lhs.m_title < rhs.m_title;
		}

		std::string m_title;
		std::chrono::seconds m_total = std::chrono::seconds{0};
	};


	static std::chrono::seconds GetTotal(const ApplicationInfo& windowInfo)
	{
		std::chrono::seconds total = std::chrono::seconds{0};
		for (const auto& tab : windowInfo.m_tabs)
		{
			total += tab.m_total;
		}
		return total;
	}


	friend bool operator<(const ApplicationInfo& lhs, const ApplicationInfo& rhs)
	{
		return lhs.m_name < rhs.m_name;
	}


	std::string m_name;
	std::set<TabInfo> m_tabs;
};


Display* open_display()
{
	return XOpenDisplay(nullptr);
}


std::optional<Window> get_focus_window(Display* pDisplay)
{
	int revert_to = 0;
	Window window;
	XGetInputFocus(pDisplay, &window, &revert_to);
	return window;
}


std::optional<Window> get_top_window(Display* pDisplay, Window start)
{
	Window current = start;
	Window parent = start;
	Window root = None;
	Window* pChildren;
	unsigned int nChildren;
	Status s;

	while (parent != root)
	{
		current = parent;
		s = XQueryTree(pDisplay, current, &root, &parent, &pChildren, &nChildren);

		if (s)
		{
			XFree(pChildren);
		}
	}

	return current;
}


std::optional<Window> get_named_window(Display* pDisplay, Window start)
{
	Window window;
	window = XmuClientWindow(pDisplay, start);
	return window;
}


std::optional<std::string> get_window_name(Display* pDisplay, Window window)
{
	XTextProperty prop;

	auto status = XGetWMName(pDisplay, window, &prop); // see man
	int count = 0, result;
	char** list = nullptr;

	Xutf8TextPropertyToTextList(pDisplay, &prop, &list, &count);
	return list[0];
}


std::optional<std::string> get_window_application(Display* d, Window w)
{
	auto x = XAllocClassHint();
	auto status = XGetClassHint(d, w, x);
	return x->res_name;
}


std::pair<std::string, std::string> get_window_info(Display* d, Window w)
{
	return {*get_window_application(d, w), *get_window_name(d, w)};
}


std::optional<std::pair<std::string, std::string>> get_active_window_info()
{
	auto pDisplay = open_display();

	auto window = get_focus_window(pDisplay);

	window = get_top_window(pDisplay, *window);
	window = get_named_window(pDisplay, *window);

	return get_window_info(pDisplay, *window);
}


int main()
{
	setlocale(LC_ALL, "");
	std::set<ApplicationInfo> applications;

	ApplicationInfo previous;

	auto find_window = [](const std::string& application_name)
	{
		return [&app_name = application_name](const ApplicationInfo& w) -> bool
		{
			return w.m_name == app_name;
		};
	};

#pragma clang diagnostic push
#pragma ide diagnostic ignored "EndlessLoop"
	while (true)
	{
		auto window_info = get_active_window_info();

		if (window_info)
		{
			auto it = std::find_if(
					std::begin(applications)
					, std::end(applications)
					, find_window(window_info->first));

			if (it == applications.end())
			{
				ApplicationInfo info;
				info.m_name = window_info->first;
				info.m_tabs.emplace(ApplicationInfo::TabInfo{window_info->second});

				applications.emplace(info);
			}
			else
			{
				auto itTab = std::find_if(
						std::begin(it->m_tabs)
						, std::end(it->m_tabs)
						, [&title = window_info->second](const ApplicationInfo::TabInfo& tabInfo)
						{
							return tabInfo.m_title == title;
						});

				if (itTab == it->m_tabs.end())
				{
					it._M_const_cast()->m_tabs.emplace(ApplicationInfo::TabInfo{window_info->second, std::chrono::seconds{1}});
				}
				else
				{
					itTab._M_const_cast()->m_total += std::chrono::seconds{1};
				}
			}
		}

		std::system("clear");

		for (const auto& application : applications)
		{
			std::cout
					<< "\n"
					<< application.m_name;
			for (const auto& tab : application.m_tabs)
			{
				std::cout << "\n\t" << tab.m_title << " : " << tab.m_total.count() << "s";
			}

			std::cout << "\n\n";
		}

		std::this_thread::sleep_for(std::chrono::seconds{1});
	}
#pragma clang diagnostic pop

	return EXIT_SUCCESS;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.17)
project(att)

set(CMAKE_CXX_STANDARD 20)

find_package(X11 REQUIRED)


set(INCLUDE_DIRECTORIES
        ${X11_Xmu_INCLUDE_PATH}
        ${X11_INCLUDE_DIR}
        )


set(LIBRARIES
        ${X11_Xmu_LIB}
        ${X11_LIBRARIES}
        )

add_executable(att main.cpp)

target_include_directories(att PRIVATE ${INCLUDE_DIRECTORIES})
target_link_libraries(att ${LIBRARIES})

UPD Причём для Brave всё работает нормально:

total time spent: 0 minutes
brave-browser : 29s
        Input/output with files - C++ Tutorials - Brave : 4s
        SQLite Documentation - Brave : 3s
        c++ 20 write to file - Поиск в Google - Brave : 14s
        nomeata/arbtt: arbtt, the automatic rule-based time-tracker - Brave : 2s
        тест тест тест - Поиск в Google - Brave : 6s


konsole : 22s
        cmake-build-debug : att : 15s
        cmake-build-debug : tail : 7s
        cmake-build-debug : zsh : 0s


Последнее исправление: xB3JMJou (всего исправлений: 1)

Сконвертировать русские строки в UTF-8.

X512 ★★★★★
()
Ответ на: комментарий от X512

В идеале хотелось бы получить решение, которое работало на любой машине, а не только на моей

 ✘  ~/projects/att/cmake-build-debug   master  locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=ru_RU.UTF-8
LC_TIME=ru_RU.UTF-8
LC_COLLATE="en_US.UTF-8"
LC_MONETARY=ru_RU.UTF-8
LC_MESSAGES="en_US.UTF-8"
LC_PAPER=ru_RU.UTF-8
LC_NAME=ru_RU.UTF-8
LC_ADDRESS=ru_RU.UTF-8
LC_TELEPHONE=ru_RU.UTF-8
LC_MEASUREMENT=ru_RU.UTF-8
LC_IDENTIFICATION=ru_RU.UTF-8
LC_ALL=

xB3JMJou
() автор топика

UPD Причём для Brave всё работает нормально

Возможно у spotify кодировка другая.

X512 ★★★★★
()
Ответ на: комментарий от fsb4000

Чёт не прокатило, даже на питоне:

#!/usr/bin/env python

title = "куку´"
title_as_bytes = title.encode()

print(title_as_bytes.decode("ISO-8859-1"))

Вывод:

куку

Аналогичная фигня на цпп:

#include <boost/locale.hpp>

std::string convert(const std::string& str)
{
	return boost::locale::conv::to_utf<char>(str, "ISO-8859-1");
}

int main()
{
	std::cout << convert("кÑ\u0083кÑ\u0083") << std::endl;
	return EXIT_SUCCESS;
}

Вывод:

куку

Видимо как-то глобально туплю, но сейчас уже голова не соображает.

Видимо либо кодировка не та, либо я чего-то не вижу совсем.

Пробежался по всем возможным кодировкам - не получается адекватная строка (должно получиться куку).

xB3JMJou
() автор топика
Ответ на: комментарий от xB3JMJou

Ну и вообще как-то это можно спрятать? Может есть библиотеки? Мне не важна тут производительность, я хочу получать строку в любой кодировке и фигачить её в utf8, делать я это буду раз в секунду и только для одного окна.

Может быть есть уже готовые решения?

xB3JMJou
() автор топика
Ответ на: комментарий от xB3JMJou

Пробежался по всем возможным кодировкам - не получается адекватная строка (должно получиться куку).

Это CP-1252, Win-1252, но корявый. Корявый возможно потому что непечатные символы терминал скопировать не даёт.

$ echo "Ðвезда по имени СолнÑе" | iconv -t cp1252
�везда по имени Солн�е

$ echo "кÑкѴ" | iconv -t cp1252
к�кѴ
EXL ★★★★★
()
Ответ на: комментарий от EXL

В общем у меня какая-то странная фигня происходит: Захожу на сайт http://string-functions.com/encodedecode.aspx, конверчу utf-8 строку ‘звезда по имени солнце’ в iso-8859-1, чтобы проверить что сайт не кривой открываю: http://www.online-decoder.com/ru и ввожу полученную строку в кодировке iso-8859-1 «Ð·Ð²ÐµÐ·Ð´Ð° по имени солнце» => получаю ровно то что и ожидал (строку ‘звезда по имени солнце’).

Далее иду в консоль (в настройках стоит кодировка utf-8) и пытаюсь сделать ровно то что провернул с помощью сайта (даже не использую cpp или python:

 ~/projects/att/cmake-build-debug   xcb ●  echo "звезда по имени ÑолнÑе" | iconv -f iso-8859-1 -t UTF-8
÷òõ÷ôð ÿþ øüõýø ÃþûýÃõ
 ~/projects/att/cmake-build-debug   xcb ●  
 ~/projects/att/cmake-build-debug   xcb ●  echo "звезда по имени ÑолнÑе" | iconv -f latin1 -t UTF-8
÷òõ÷ôð ÿþ øüõýø ÃþûýÃõ

Из этого напрашивается вывод что проблема в моём окружении/консоли?

xB3JMJou
() автор топика
Ответ на: комментарий от xB3JMJou

А вот тут я чёт сломался:

~/projects/att/cmake-build-debug   xcb ●  echo "звезда по имени ÑолнÑе" | iconv -t iso-8859-1 -f UTF-8
звезда по имени �олн�е

При этом:

~/projects/att/cmake-build-debug   xcb ●  iconv --help
Usage: iconv [OPTION...] [FILE...]
Convert encoding of given files from one encoding to another.

 Input/Output format specification:
  -f, --from-code=NAME       encoding of original text
  -t, --to-code=NAME         encoding for output

Т.е. -f - кодировка исходного текста -t - в какую кодировку я хочу сконвертить исходный текст.

xB3JMJou
() автор топика
Ответ на: комментарий от xB3JMJou

Из этого напрашивается вывод что проблема в моём окружении/консоли?

Не, всё норм.

  1. Сохраняем текст как utf-8: https://imgur.com/a/HPHpKUx

  2. Делаем сохранить как windows1252: https://imgur.com/a/qdAfOtc

  3. Нажимаем «Повторно открыть в utf-8»: https://imgur.com/a/iVdonp9

Как видишь в Visual Code на Windows всё тоже самое, так что нет проблем в твоём окружении/бага в iconv.

Кстати:

В Microsoft Windows для западноевропейских языков используется кодировка Windows-1252, которая отличается от ISO-8859-1 тем, что позиции 128—159 (0x80—0x9F) здесь заняты разными полезными типографскими символами. Большинство браузеров не различают ISO-8859-1 и Windows-1252 — фактически, и в том, и в другом случае они отображают текст как Windows-1252.

https://ru.wikipedia.org/wiki/ISO_8859-1

fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 2)
Ответ на: комментарий от fsb4000

Да, действительно похоже на багу в iconv, спасибо за ответ.

Для примера команда:

watch -n 1 'xdotool getactivewindow getwindowname'

Выдаёт адекватную строку.

Расширяя тогда исходный вопрос - что можете подсказать по работе с кодировкой?

Как я понимаю не обязательно у всех в spotify будет ISO-8859-1 кодировка :)

Как вообще с этим работают в C++? Может быть есть какая-то либа, которая берёт на себя всю работу и гарантирует что с некоторой вероятностью (больше 50%) на выходе будет валидная/читаемая utf-8 строка?

Понятное дело что я могу под себя захардкодить, но хочется сразу как-то шире смотреть на проблему, я уверен что не первый наткнулся на такую фигню.

xB3JMJou
() автор топика
Ответ на: комментарий от xB3JMJou

Да, действительно похоже на багу в iconv, спасибо за ответ.

Извини что не совсем точно выразился.

Я имел ввиду нет проблем в твоём окружении и нет бага в iconv

Из всей этой темы я бы подумал что баг в spotify.

  1. spotify считал название файла:
b7d0 b2d0 b5d0 b7d0 b4d0 b0d0 d020 d0bf
20be b8d0 bcd0 b5d0 bdd0 b8d0 d120 d081
d0be d0bb d1bd d086 00b5
  1. Эти байты в разных кодировках (то есть без всякий преобразований, просто смотрим на эти байты, как будто это текст в этих кодировках)
# cp1252/ISO-8859-1
звезда по имени �олнце
# utf-8
звезда по имени солнце
# utf-16
럐닐뗐럐듐냐퀠킿₾룐볐뗐뷐룐턠킁킾킻톽킆
# cp1251
звезда по имени солнце
  1. spotify решает, что это текст в cp1252/

  2. x11 нужна строка в utf-8.

  3. spotify переводит исходную строку в utf-8:

#utf-8
звезда по имени �олнце

или

90c3 b7c2 90c3 b2c2 90c3 b5c2 90c3 b7c2
90c3 b4c2 90c3 b0c2 c320 c290 c3bf c290
20be 90c3 b8c2 90c3 bcc2 90c3 b5c2 90c3
bdc2 90c3 b8c2 c320 ef91 bdbf 90c3 bec2
90c3 bbc2 90c3 bdc2 91c3 80e2 c3a0 c290
00b5
  1. Ты переводишь эту строку из utf-8 в cp1252:
#cp1252
звезда по имени �олнце

или

b7d0 b2d0 b5d0 b7d0 b4d0 b0d0 d020 d0bf
20be b8d0 bcd0 b5d0 bdd0 b8d0 d120 d09d
d0be d0bb d1bd d086 00b5
  1. Теперь ты показываешь эти байты, как будто это utf-8 (без всякой дополнительной конвертации)
#utf-8
звезда по имени ѝолнце

Как вообще с этим работают в C++? Может быть есть какая-то либа, которая берёт на себя всю работу и гарантирует что с некоторой вероятностью (больше 50%) на выходе будет валидная/читаемая utf-8 строка?

Я для перевода из одной кодовой страницы в другую пользовался только IconV или glib обёрткой для IconV: https://developer.gnome.org/glib/stable/glib-Character-Set-Conversion.html

Но я не угадывал кодировку файла, а у меня ресурсы были в (utf-8(несколько языков) или английский), и я в зависимости от локали которая была у пользователя либо сразу выводил в utf-8 нужный язык, либо делал дополнительное преобразование(чтобы программа не ломалась, если у пользователя KOI-8R, или cp866 или cp1251)

fsb4000 ★★★★★
()

Кури как работают кодировки, потому что ты не понимаешь и от этого шляпа.

peregrine ★★★★★
()

Похоже тебе нужно id3v1 тэги в мп3шках сконвертить в id3v2. Для этого есть готовый софт

cobold ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.